From 66084b816dd25a41f774240328a31d57efd276e4 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 17 Sep 2009 03:18:51 +0000 Subject: Begin libsandglass implementation. --- configure.ac | 20 +++- foo.c | 4 + src/Makefile.am | 4 +- src/sandglass.c | 249 ++++++++++++++++++++++++++++++++++++++++++++++++ src/sandglass.h | 13 +-- src/sandglass_impl.h | 39 ++++++++ src/tsc.c | 39 ++++++++ src/x86/tsc-x86.s | 17 ++++ src/x86_64/tsc-x86_64.s | 17 ++++ tests/simple-test | 2 +- tests/simple.c | 27 ++++++ 11 files changed, 416 insertions(+), 15 deletions(-) create mode 100644 foo.c create mode 100644 src/sandglass_impl.h create mode 100644 src/tsc.c diff --git a/configure.ac b/configure.ac index 044ea79..deea2b8 100644 --- a/configure.ac +++ b/configure.ac @@ -17,8 +17,8 @@ dnl License along with this program. If not, see dnl . AC_PREREQ(2.63) -AC_INIT(The Sandglass Library, 0.0.0, tavianator@gmail.com, libsandglass) -AM_INIT_AUTOMAKE(libsandglass, 0.0.0) +AC_INIT(The Sandglass Library, 0.0, tavianator@gmail.com, libsandglass) +AM_INIT_AUTOMAKE(libsandglass, 0.0) dnl Programs AC_PROG_CC @@ -39,8 +39,20 @@ AC_TYPE_SIZE_T dnl Find out which assembly files to compile AC_CANONICAL_HOST -AM_CONDITIONAL([X86], [echo $host_cpu | grep '.*86$' >/dev/null]) -AM_CONDITIONAL([X86_64], [echo $host_cpu | grep 'x86_64$' >/dev/null]) +if echo $host_cpu | grep '86$' 2>&1 >/dev/null +then + arch_x86="true" + AC_DEFINE([SANDGLASS_TSC]) +fi + +if echo $host_cpu | grep 'x86_64$' 2>&1 >/dev/null +then + arch_x86_64="true" + AC_DEFINE([SANDGLASS_TSC]) +fi + +AM_CONDITIONAL([X86], [test "$arch_x86"]) +AM_CONDITIONAL([X86_64], [test "$arch_x86_64"]) dnl Generate Makefiles AC_CONFIG_MACRO_DIR([m4]) diff --git a/foo.c b/foo.c new file mode 100644 index 0000000..9bdde01 --- /dev/null +++ b/foo.c @@ -0,0 +1,4 @@ + +#ifndef PRED +#error HI +#endif diff --git a/src/Makefile.am b/src/Makefile.am index 4fc85ab..476c855 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,7 +21,7 @@ lib_LTLIBRARIES = libsandglass.la nobase_include_HEADERS = sandglass.h -libsandglass_la_SOURCES = sandglass.h sandglass.c +libsandglass_la_SOURCES = sandglass.h sandglass_impl.h sandglass.c tsc.c if X86 libsandglass_la_SOURCES += x86/tsc-x86.s @@ -30,3 +30,5 @@ endif if X86_64 libsandglass_la_SOURCES += x86_64/tsc-x86_64.s endif + +libsandglass_la_LIBADD = -lrt diff --git a/src/sandglass.c b/src/sandglass.c index 4acf983..57729f9 100644 --- a/src/sandglass.c +++ b/src/sandglass.c @@ -18,4 +18,253 @@ * . * *************************************************************************/ +#include "sandglass_impl.h" #include "sandglass.h" +#include +#include +#include + +static int sandglass_real_create(sandglass_t *sandglass, + const sandglass_attributes_t *attr); + +/* Create a timer */ +int +sandglass_create(sandglass_t *sandglass, + const sandglass_attributes_t *min, + const sandglass_attributes_t *max) +{ + sandglass_attributes_t realmin, realmax; + + /* Get our real min and max values */ + + if (min) + realmin = *min; + else { + /* min defaults to { SANDGLASS_INTROSPECTIVE, SANDGLASS_SYSTEM } */ + realmin.incrementation = SANDGLASS_INTROSPECTIVE; + realmin.resolution = SANDGLASS_SYSTEM; + } + + if (max) + realmax = *max; + else { + /* max defaults to the greater of min and { SANDGLASS_INTROSPECTIVE, + SANDGLASS_CPUTIME } */ + if (realmin.incrementation > SANDGLASS_INTROSPECTIVE + || (realmin.incrementation == SANDGLASS_INTROSPECTIVE + && realmin.resolution > SANDGLASS_CPUTIME)) + realmax = realmin; + else { + realmax.incrementation = SANDGLASS_INTROSPECTIVE; + realmax.resolution = SANDGLASS_CPUTIME; + } + } + + /* Ensure max >= min */ + if (realmax.incrementation < realmin.incrementation + || (realmax.incrementation == realmin.incrementation + && realmax.resolution < realmin.resolution)) + { + errno = EINVAL; + return -1; + } + + /* Now search for available timers, starting from max */ + + while (sandglass_real_create(sandglass, &realmax) != 0) { + /* Once we reach the minimum attributes, bail out */ + if (realmax.incrementation == realmin.incrementation + && realmax.resolution == realmin.resolution) + { + errno = ENOTSUP; + return -1; + } + + /* Try the next lowest allowable settings */ + if (realmax.resolution) + --realmax.resolution; + else { + if (realmax.incrementation) { + --realmax.incrementation; + realmax.resolution = SANDGLASS_CPUTIME; + } else { + errno = ENOTSUP; + return -1; + } + } + } + + return 0; +} + +static int +sandglass_real_create(sandglass_t *sandglass, + const sandglass_attributes_t *attr) +{ + switch (attr->incrementation) { + case SANDGLASS_MONOTONIC: + switch (attr->resolution) { + case SANDGLASS_REALTICKS: +#ifdef SANDGLASS_TSC + sandglass->resolution = sandglass_tsc_resolution(); + sandglass->loops = sandglass_tsc_loops(); + break; +#else + return -1; +#endif + + case SANDGLASS_CPUTIME: +#ifdef SANDGLASS_TSC + sandglass->resolution = sandglass_tsc_resolution(); + sandglass->loops = 1; + break; +#else + return -1; +#endif + + case SANDGLASS_SYSTEM: + sandglass->resolution = 1e9; + sandglass->loops = 1; + break; + + default: + return -1; + } + break; + + case SANDGLASS_INTROSPECTIVE: + switch (attr->resolution) { + case SANDGLASS_REALTICKS: + /* No such thing as an introspective raw TSC */ + return -1; + + case SANDGLASS_CPUTIME: + if (sysconf(_SC_THREAD_CPUTIME) > 0 || sysconf(_SC_CPUTIME) > 0) { + sandglass->resolution = 1e9; + sandglass->loops = 1; + } else + return -1; + break; + + case SANDGLASS_SYSTEM: + sandglass->resolution = CLOCKS_PER_SEC; + sandglass->loops = 1; + break; + + default: + return -1; + } + break; + + default: + return -1; + } + + sandglass->attributes = *attr; + return 0; +} + +/* Store a timer value in sandglass->grains */ +static int sandglass_real_gettime(sandglass_t *sandglass); + +/* Start timing */ +int +sandglass_begin(sandglass_t *sandglass) +{ + return sandglass_real_gettime(sandglass); +} + +/* Finish timing */ +int +sandglass_elapse(sandglass_t *sandglass) +{ + long oldgrains = sandglass->grains; + + if (sandglass_real_gettime(sandglass) != 0) + return -1; + + sandglass->grains -= oldgrains; + sandglass->grains /= sandglass->loops; + + return 0; +} + +/* Store a timer value in sandglass->grains */ +static int +sandglass_real_gettime(sandglass_t *sandglass) +{ + struct timespec ts; + clock_t clock_ticks; + + switch (sandglass->attributes.incrementation) { + case SANDGLASS_MONOTONIC: + switch (sandglass->attributes.resolution) { + case SANDGLASS_REALTICKS: +#ifdef SANDGLASS_TSC + sandglass->grains = sandglass_get_tsc(); + break; +#else + return -1; +#endif + + case SANDGLASS_CPUTIME: +#ifdef SANDGLASS_TSC + sandglass->grains = sandglass_get_tsc(); + break; +#else + return -1; +#endif + + case SANDGLASS_SYSTEM: + if (sysconf(_SC_MONOTONIC_CLOCK) > 0) { + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) + return -1; + sandglass->grains = ts.tv_nsec; + } else { + if (clock_gettime(CLOCK_REALTIME, &ts) != 0) + return -1; + sandglass->grains = ts.tv_nsec; + } + break; + + default: + return -1; + } + break; + + case SANDGLASS_INTROSPECTIVE: + switch (sandglass->attributes.resolution) { + case SANDGLASS_REALTICKS: + /* No such thing as an introspective raw TSC */ + return -1; + + case SANDGLASS_CPUTIME: + if (sysconf(_SC_THREAD_CPUTIME) > 0) { + if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) != 0) + return -1; + sandglass->grains = ts.tv_nsec; + } else if (sysconf(_SC_CPUTIME) > 0) { + if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) != 0) + return -1; + sandglass->grains = ts.tv_nsec; + } else + return -1; + break; + + case SANDGLASS_SYSTEM: + if ((clock_ticks = clock()) == -1) + return -1; + sandglass->grains = clock(); + break; + + default: + return -1; + } + break; + + default: + return -1; + } + + return 0; +} diff --git a/src/sandglass.h b/src/sandglass.h index a9e99e5..3cd14ba 100644 --- a/src/sandglass.h +++ b/src/sandglass.h @@ -93,7 +93,7 @@ struct sandglass_t long grains; /* grains/resolution should give elapsed time in seconds */ - long resolution; + double resolution; /* * Internal fields @@ -140,20 +140,15 @@ int sandglass_elapse(sandglass_t *sandglass); do { \ routine; \ sandglass_begin(sandglass); \ - for ((sandglass)->i = 0; (sandglass)->i < (sandglass)->loops; ++i) { \ + for ((sandglass)->i = 0; \ + (sandglass)->i < (sandglass)->loops; \ + ++(sandglass)->i) { \ SANDGLASS_NO_UNROLL(); \ routine; \ } \ sandglass_elapse(sandglass); \ } while (0) -/* - * Low-level API - */ - -/* Read the time stamp counter */ -long sandglass_get_tsc(); - #ifdef __cplusplus } #endif diff --git a/src/sandglass_impl.h b/src/sandglass_impl.h new file mode 100644 index 0000000..5a3650e --- /dev/null +++ b/src/sandglass_impl.h @@ -0,0 +1,39 @@ +/************************************************************************* + * Copyright (C) 2008 Tavian Barnes * + * * + * This file is part of The Sandglass Library. * + * * + * The Sandglass Library is free software; you can redistribute it * + * and/or modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either version * + * 3 of the License, or (at your option) any later version. * + * * + * The Sandglass Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/* + * Internal libsandglass API + */ + +#ifndef SANDGLASS_IMPL_H_INCLUDED +#define SANDGLASS_IMPL_H_INCLUDED + +#include "sandglass.h" + +#ifdef SANDGLASS_TSC +/* Read the time stamp counter */ +long sandglass_get_tsc(); +/* Get the timing resolution of the TSC */ +double sandglass_tsc_resolution(); +/* Get the necessary number of loops for SANDGLASS_REALTICKS */ +unsigned int sandglass_tsc_loops(); +#endif + +#endif /* SANDGLASS_IMPL_H_INCLUDED */ \ No newline at end of file diff --git a/src/tsc.c b/src/tsc.c new file mode 100644 index 0000000..64fe1b1 --- /dev/null +++ b/src/tsc.c @@ -0,0 +1,39 @@ +/************************************************************************* + * Copyright (C) 2008 Tavian Barnes * + * * + * This file is part of The Sandglass Library. * + * * + * The Sandglass Library is free software; you can redistribute it * + * and/or modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either version * + * 3 of the License, or (at your option) any later version. * + * * + * The Sandglass Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +#include "sandglass_impl.h" +#include "sandglass.h" +#include + +/* Gets the number of clock ticks per second */ +double +sandglass_tsc_resolution() +{ + static long tsc = 0; + struct timespec ts = { .tv_sec = 0, .tv_nsec = 100000000 }; + + if (tsc == 0) { + tsc = sandglass_get_tsc(); + while (nanosleep(&ts, &ts) != 0); + tsc = sandglass_get_tsc() - tsc; + } + + return tsc*10.0; +} diff --git a/src/x86/tsc-x86.s b/src/x86/tsc-x86.s index 570b8af..46013bb 100644 --- a/src/x86/tsc-x86.s +++ b/src/x86/tsc-x86.s @@ -38,3 +38,20 @@ sandglass_get_tsc: popl %ebx ret .size sandglass_get_tsc, .-sandglass_get_tsc + +/* + * Return the granularity of the TSC + */ + +/* unsigned int sandglass_tsc_loops(); */ +.globl sandglass_tsc_loops + .type sandglass_tsc_loops, @function +sandglass_tsc_loops: + rdtsc /* Read time stamp counter */ + movl %eax, %ecx +.Lrdtsc: + rdtsc /* Read counter again */ + subl %ecx, %eax + jz .Lrdtsc /* If we got the same value, try again */ + ret + .size sandglass_tsc_loops, .-sandglass_tsc_loops diff --git a/src/x86_64/tsc-x86_64.s b/src/x86_64/tsc-x86_64.s index c585719..52f02c2 100644 --- a/src/x86_64/tsc-x86_64.s +++ b/src/x86_64/tsc-x86_64.s @@ -38,3 +38,20 @@ sandglass_get_tsc: movq %rdi, %rbx ret .size sandglass_get_tsc, .-sandglass_get_tsc + +/* + * Return the granularity of the TSC + */ + +/* unsigned int sandglass_tsc_loops(); */ +.globl sandglass_tsc_loops + .type sandglass_tsc_loops, @function +sandglass_tsc_loops: + rdtsc /* Read time stamp counter */ + movl %eax, %ecx +.Lrdtsc: + rdtsc /* Read counter again */ + subl %ecx, %eax + jz .Lrdtsc /* If we got the same value, try again */ + ret + .size sandglass_tsc_loops, .-sandglass_tsc_loops diff --git a/tests/simple-test b/tests/simple-test index 407febc..8fbcf16 100755 --- a/tests/simple-test +++ b/tests/simple-test @@ -32,7 +32,7 @@ DUALCASE=1; export DUALCASE # for MKS sh # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH -relink_command="(cd /home/tavianator/libsandglass/tests; { test -z \"\${LIBRARY_PATH+set}\" || unset LIBRARY_PATH || { LIBRARY_PATH=; export LIBRARY_PATH; }; }; { test -z \"\${COMPILER_PATH+set}\" || unset COMPILER_PATH || { COMPILER_PATH=; export COMPILER_PATH; }; }; { test -z \"\${GCC_EXEC_PREFIX+set}\" || unset GCC_EXEC_PREFIX || { GCC_EXEC_PREFIX=; export GCC_EXEC_PREFIX; }; }; { test -z \"\${LD_RUN_PATH+set}\" || unset LD_RUN_PATH || { LD_RUN_PATH=; export LD_RUN_PATH; }; }; { test -z \"\${LD_LIBRARY_PATH+set}\" || unset LD_LIBRARY_PATH || { LD_LIBRARY_PATH=; export LD_LIBRARY_PATH; }; }; PATH=/bin:/usr/bin:/sbin:/usr/sbin:/opt/java/bin:/opt/java/jre/bin:/home/tavianator/bin:/usr/bin/perlbin/site:/usr/bin/perlbin/vendor:/usr/bin/perlbin/core:/opt/qt/bin; export PATH; gcc -g -Wall -o \$progdir/\$file simple.o ../src/.libs/libsandglass.so -Wl,-rpath -Wl,/home/tavianator/libsandglass/src/.libs)" +relink_command="(cd /home/tavianator/libsandglass/tests; { test -z \"\${LIBRARY_PATH+set}\" || unset LIBRARY_PATH || { LIBRARY_PATH=; export LIBRARY_PATH; }; }; { test -z \"\${COMPILER_PATH+set}\" || unset COMPILER_PATH || { COMPILER_PATH=; export COMPILER_PATH; }; }; { test -z \"\${GCC_EXEC_PREFIX+set}\" || unset GCC_EXEC_PREFIX || { GCC_EXEC_PREFIX=; export GCC_EXEC_PREFIX; }; }; { test -z \"\${LD_RUN_PATH+set}\" || unset LD_RUN_PATH || { LD_RUN_PATH=; export LD_RUN_PATH; }; }; { test -z \"\${LD_LIBRARY_PATH+set}\" || unset LD_LIBRARY_PATH || { LD_LIBRARY_PATH=; export LD_LIBRARY_PATH; }; }; PATH=/bin:/usr/bin:/sbin:/usr/sbin:/opt/java/bin:/opt/java/jre/bin:/home/tavianator/bin:/usr/bin/perlbin/site:/usr/bin/perlbin/vendor:/usr/bin/perlbin/core:/opt/qt/bin; export PATH; gcc -g -Wall -o \$progdir/\$file simple.o ../src/.libs/libsandglass.so -lrt -Wl,-rpath -Wl,/home/tavianator/libsandglass/src/.libs)" # This environment variable determines our operation mode. if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then diff --git a/tests/simple.c b/tests/simple.c index 921ab6c..85dacb6 100644 --- a/tests/simple.c +++ b/tests/simple.c @@ -19,10 +19,37 @@ *************************************************************************/ #include +#include +#include #include +#include int main() { + sandglass_t sandglass; + sandglass_attributes_t min = { SANDGLASS_MONOTONIC, SANDGLASS_SYSTEM }, + max = { SANDGLASS_MONOTONIC, SANDGLASS_CPUTIME }; + struct timespec tosleep = { .tv_sec = 0, .tv_nsec = 100000000 }; + + if (sandglass_create(&sandglass, &min, &max) != 0) { + perror("sandglass_create()"); + return EXIT_FAILURE; + } + + if (sandglass_begin(&sandglass) != 0) { + perror("sandglass_begin()"); + return EXIT_FAILURE; + } + while (nanosleep(&tosleep, &tosleep) != 0); + if (sandglass_elapse(&sandglass) != 0) { + perror("sandglass_elapse()"); + return EXIT_FAILURE; + } + + printf("0.1 seconds timed by sandglass as %ld grains; %g s\n", + sandglass.grains, + sandglass.grains/sandglass.resolution); + return EXIT_SUCCESS; } -- cgit v1.2.3