From c223da83e253b0057bb029bf4fbb55a05844215c Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 24 Mar 2010 13:23:53 +0100 Subject: [PATCH] Add support for specific clock sources Signed-off-by: Jens Axboe --- README | 2 + arch/arch-x86.h | 10 +++++ arch/arch-x86_64.h | 10 +++++ debug.h | 1 + fio.h | 22 ++-------- gettime.c | 106 ++++++++++++++++++++++++++++++++++++++++++--- init.c | 1 + options.c | 33 ++++++++++++++ time.c | 6 ++- time.h | 32 ++++++++++++++ 10 files changed, 197 insertions(+), 26 deletions(-) create mode 100644 time.h diff --git a/README b/README index d5235de3..e64853ea 100644 --- a/README +++ b/README @@ -127,6 +127,8 @@ options in fio. Currently the options are: diskutil Dump info related to disk utilization updates job:x Dump info only related to job number x mutex Dump info only related to mutex up/down ops + profile Dump info related to profile extensions + time Dump info related to internal time keeping ? or help Show available debug options. You can specify as many as you want, eg --debug=file,mem will enable diff --git a/arch/arch-x86.h b/arch/arch-x86.h index 9631437e..bffd1dd8 100644 --- a/arch/arch-x86.h +++ b/arch/arch-x86.h @@ -38,7 +38,17 @@ static inline unsigned long arch_ffz(unsigned long bitmask) __asm__("bsfl %1,%0" :"=r" (bitmask) :"r" (~bitmask)); return bitmask; } + +static inline unsigned long long get_cpu_clock(void) +{ + unsigned long ret; + + __asm__ __volatile__("rdtsc" : "=A" (ret)); + return ret; +} + #define ARCH_HAVE_FFZ #define ARCH_HAVE_SSE +#define ARCH_HAVE_CPU_CLOCK #endif diff --git a/arch/arch-x86_64.h b/arch/arch-x86_64.h index 457714c8..3ea8070e 100644 --- a/arch/arch-x86_64.h +++ b/arch/arch-x86_64.h @@ -38,7 +38,17 @@ static inline unsigned int arch_ffz(unsigned int bitmask) __asm__("bsfl %1,%0" :"=r" (bitmask) :"r" (~bitmask)); return bitmask; } + +static inline unsigned long long get_cpu_clock(void) +{ + unsigned int lo, hi; + + __asm__ __volatile__("rdtsc" : "=a" (lo), "=d" (hi)); + return ((unsigned long long) hi << 32ULL) | lo; +} + #define ARCH_HAVE_FFZ #define ARCH_HAVE_SSE +#define ARCH_HAVE_CPU_CLOCK #endif diff --git a/debug.h b/debug.h index e51d8b20..3473d6f7 100644 --- a/debug.h +++ b/debug.h @@ -17,6 +17,7 @@ enum { FD_JOB, FD_MUTEX, FD_PROFILE, + FD_TIME, FD_DEBUG_MAX, }; diff --git a/fio.h b/fio.h index 05911c00..5038e4de 100644 --- a/fio.h +++ b/fio.h @@ -31,6 +31,7 @@ #include "helpers.h" #include "options.h" #include "profile.h" +#include "time.h" #ifdef FIO_HAVE_GUASI #include @@ -238,6 +239,7 @@ struct thread_options { unsigned int gtod_reduce; unsigned int gtod_cpu; unsigned int gtod_offload; + enum fio_cs clocksource; char *read_iolog_file; char *write_iolog_file; @@ -481,6 +483,7 @@ extern unsigned long done_secs; extern char *job_section; extern int fio_gtod_offload; extern int fio_gtod_cpu; +extern enum fio_cs fio_clock_source; extern struct thread_data *threads; @@ -517,25 +520,6 @@ static inline int should_fsync(struct thread_data *td) return 0; } -/* - * Time functions - */ -extern unsigned long long utime_since(struct timeval *, struct timeval *); -extern unsigned long long utime_since_now(struct timeval *); -extern unsigned long mtime_since(struct timeval *, struct timeval *); -extern unsigned long mtime_since_now(struct timeval *); -extern unsigned long time_since_now(struct timeval *); -extern unsigned long mtime_since_genesis(void); -extern void usec_spin(unsigned int); -extern void usec_sleep(struct thread_data *, unsigned long); -extern void fill_start_time(struct timeval *); -extern void fio_gettime(struct timeval *, void *); -extern void fio_gtod_init(void); -extern void fio_gtod_update(void); -extern void set_genesis_time(void); -extern int ramp_time_over(struct thread_data *); -extern int in_ramp_time(struct thread_data *); - /* * Init/option functions */ diff --git a/gettime.c b/gettime.c index 8ec70b9f..08d2d2b3 100644 --- a/gettime.c +++ b/gettime.c @@ -3,6 +3,7 @@ */ #include +#include #include #include "fio.h" @@ -10,14 +11,17 @@ #include "hash.h" -static int clock_gettime_works = 0; +static unsigned long cycles_per_usec; static struct timeval last_tv; +static unsigned long last_cycles; static int last_tv_valid; static struct timeval *fio_tv; int fio_gtod_offload = 0; int fio_gtod_cpu = -1; +enum fio_cs fio_clock_source = CS_GTOD; + #ifdef FIO_DEBUG_TIME #define HASH_BITS 8 @@ -124,19 +128,44 @@ void fio_gettime(struct timeval *tp, void fio_unused *caller) if (fio_tv) { memcpy(tp, fio_tv, sizeof(*tp)); return; - } else if (!clock_gettime_works) { -gtod: + } + + switch (fio_clock_source) { + case CS_GTOD: gettimeofday(tp, NULL); - } else { + break; + case CS_CGETTIME: { struct timespec ts; if (clock_gettime(CLOCK_REALTIME, &ts) < 0) { - clock_gettime_works = 0; - goto gtod; + log_err("fio: clock_gettime fails\n"); + assert(0); } tp->tv_sec = ts.tv_sec; tp->tv_usec = ts.tv_nsec / 1000; + break; + } +#ifdef ARCH_HAVE_CPU_CLOCK + case CS_CPUCLOCK: { + unsigned long long usecs, t; + + t = get_cpu_clock(); + if (t < last_cycles) { + dprint(FD_TIME, "CPU clock going back in time\n"); + t = last_cycles; + } + + usecs = t / cycles_per_usec; + tp->tv_sec = usecs / 1000000; + tp->tv_usec = usecs % 1000000; + last_cycles = t; + break; + } +#endif + default: + log_err("fio: invalid clock source %d\n", fio_clock_source); + break; } /* @@ -154,6 +183,71 @@ gtod: memcpy(&last_tv, tp, sizeof(*tp)); } +static unsigned long get_cycles_per_usec(void) +{ + struct timeval s, e; + unsigned long long c_s, c_e; + + gettimeofday(&s, NULL); + c_s = get_cpu_clock(); + do { + unsigned long long elapsed; + + gettimeofday(&e, NULL); + elapsed = utime_since(&s, &e); + if (elapsed >= 10) { + c_e = get_cpu_clock(); + break; + } + } while (1); + + return c_e - c_s; +} + +void fio_clock_init(void) +{ + double delta, mean, S; + unsigned long avg, cycles[10]; + int i, samples; + + last_tv_valid = 0; + + cycles[0] = get_cycles_per_usec(); + S = delta = mean = 0.0; + for (i = 0; i < 10; i++) { + cycles[i] = get_cycles_per_usec(); + delta = cycles[i] - mean; + if (delta) { + mean += delta / (i + 1.0); + S += delta * (cycles[i] - mean); + } + } + + S = sqrt(S / (10 - 1.0)); + + samples = avg = 0; + for (i = 0; i < 10; i++) { + double this = cycles[i]; + + if ((max(this, mean) - min(this, mean)) > S) + continue; + samples++; + avg += this; + } + + S /= 10.0; + mean /= 10.0; + + for (i = 0; i < 10; i++) + dprint(FD_TIME, "cycles[%d]=%lu\n", i, cycles[i] / 10); + + avg /= (samples * 10); + dprint(FD_TIME, "avg: %lu\n", avg); + dprint(FD_TIME, "mean=%f, S=%f\n", mean, S); + + cycles_per_usec = avg; +} + void fio_gtod_init(void) { fio_tv = smalloc(sizeof(struct timeval)); diff --git a/init.c b/init.c index 3d48467c..9163e475 100644 --- a/init.c +++ b/init.c @@ -992,6 +992,7 @@ struct debug_level debug_levels[] = { { .name = "job", .shift = FD_JOB }, { .name = "mutex", .shift = FD_MUTEX }, { .name = "profile", .shift = FD_PROFILE }, + { .name = "time", .shift = FD_TIME }, { .name = NULL, }, }; diff --git a/options.c b/options.c index 40f60ff3..bdb56d3e 100644 --- a/options.c +++ b/options.c @@ -225,6 +225,15 @@ static int str_mem_cb(void *data, const char *mem) return 0; } +static int fio_clock_source_cb(void *data, const char *str) +{ + struct thread_data *td = data; + + fio_clock_source = td->o.clocksource; + fio_time_init(); + return 0; +} + static int str_lockmem_cb(void fio_unused *data, unsigned long *val) { mlock_size = *val; @@ -1216,6 +1225,30 @@ static struct fio_option options[FIO_MAX_OPTS] = { .off1 = td_var_offset(ramp_time), .help = "Ramp up time before measuring performance", }, + { + .name = "clocksource", + .type = FIO_OPT_STR, + .cb = fio_clock_source_cb, + .off1 = td_var_offset(clocksource), + .help = "What type of timing source to use", + .def = "gettimeofday", + .posval = { + { .ival = "gettimeofday", + .oval = CS_GTOD, + .help = "Use gettimeofday(2) for timing", + }, + { .ival = "clock_gettime", + .oval = CS_CGETTIME, + .help = "Use clock_gettime(2) for timing", + }, +#ifdef ARCH_HAVE_CPU_CLOCK + { .ival = "cpu", + .oval = CS_CPUCLOCK, + .help = "Use CPU private clock", + }, +#endif + }, + }, { .name = "mem", .alias = "iomem", diff --git a/time.c b/time.c index 6397f20e..5755b67e 100644 --- a/time.c +++ b/time.c @@ -5,6 +5,7 @@ static struct timeval genesis; static unsigned long ns_granularity; +unsigned long long genesis_cycles; unsigned long long utime_since(struct timeval *s, struct timeval *e) { @@ -150,10 +151,12 @@ int ramp_time_over(struct thread_data *td) return 0; } -static void fio_init time_init(void) +void fio_init fio_time_init(void) { int i; + fio_clock_init(); + /* * Check the granularity of the nanosleep function */ @@ -177,6 +180,7 @@ static void fio_init time_init(void) void set_genesis_time(void) { fio_gettime(&genesis, NULL); + genesis_cycles = get_cpu_clock(); } void fill_start_time(struct timeval *t) diff --git a/time.h b/time.h new file mode 100644 index 00000000..61ad5d80 --- /dev/null +++ b/time.h @@ -0,0 +1,32 @@ +#ifndef FIO_TIME_H +#define FIO_TIME_H + +/* + * Clock sources + */ +enum fio_cs { + CS_GTOD = 1, + CS_CGETTIME, + CS_CPUCLOCK, +}; + +extern unsigned long long utime_since(struct timeval *, struct timeval *); +extern unsigned long long utime_since_now(struct timeval *); +extern unsigned long mtime_since(struct timeval *, struct timeval *); +extern unsigned long mtime_since_now(struct timeval *); +extern unsigned long time_since_now(struct timeval *); +extern unsigned long mtime_since_genesis(void); +extern void usec_spin(unsigned int); +extern void usec_sleep(struct thread_data *, unsigned long); +extern void fill_start_time(struct timeval *); +extern void fio_gettime(struct timeval *, void *); +extern void fio_gtod_init(void); +extern void fio_clock_init(void); +extern void fio_gtod_update(void); +extern void set_genesis_time(void); +extern int ramp_time_over(struct thread_data *); +extern int in_ramp_time(struct thread_data *); +extern unsigned long long genesis_cycles; +extern void fio_time_init(void); + +#endif -- 2.25.1