Add support for specific clock sources
authorJens Axboe <jens.axboe@oracle.com>
Wed, 24 Mar 2010 12:23:53 +0000 (13:23 +0100)
committerJens Axboe <jens.axboe@oracle.com>
Wed, 24 Mar 2010 12:23:53 +0000 (13:23 +0100)
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
README
arch/arch-x86.h
arch/arch-x86_64.h
debug.h
fio.h
gettime.c
init.c
options.c
time.c
time.h [new file with mode: 0644]

diff --git a/README b/README
index d5235de..e64853e 100644 (file)
--- 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
index 9631437..bffd1dd 100644 (file)
@@ -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
index 457714c..3ea8070 100644 (file)
@@ -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 e51d8b2..3473d6f 100644 (file)
--- 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 05911c0..5038e4d 100644 (file)
--- 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 <guasi.h>
@@ -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
  */
index 8ec70b9..08d2d2b 100644 (file)
--- a/gettime.c
+++ b/gettime.c
@@ -3,6 +3,7 @@
  */
 
 #include <unistd.h>
+#include <math.h>
 #include <sys/time.h>
 
 #include "fio.h"
 
 #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 3d48467..9163e47 100644 (file)
--- 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, },
 };
 
index 40f60ff..bdb56d3 100644 (file)
--- 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 6397f20..5755b67 100644 (file)
--- 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 (file)
index 0000000..61ad5d8
--- /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