diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/sandglass.c | 249 | ||||
-rw-r--r-- | src/sandglass.h | 13 | ||||
-rw-r--r-- | src/sandglass_impl.h | 39 | ||||
-rw-r--r-- | src/tsc.c | 39 | ||||
-rw-r--r-- | src/x86/tsc-x86.s | 17 | ||||
-rw-r--r-- | src/x86_64/tsc-x86_64.s | 17 |
7 files changed, 368 insertions, 10 deletions
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 @@ * <http://www.gnu.org/licenses/>. * *************************************************************************/ +#include "sandglass_impl.h" #include "sandglass.h" +#include <unistd.h> +#include <time.h> +#include <errno.h> + +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 <tavianator@gmail.com> * + * * + * 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 * + * <http://www.gnu.org/licenses/>. * + *************************************************************************/ + +/* + * 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 <tavianator@gmail.com> * + * * + * 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 * + * <http://www.gnu.org/licenses/>. * + *************************************************************************/ + +#include "sandglass_impl.h" +#include "sandglass.h" +#include <time.h> + +/* 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 |