hdparm: simplify timing measurement (it wa the last last user
authorDenis Vlasenko <vda.linux@googlemail.com>
Fri, 28 Sep 2007 10:28:03 +0000 (10:28 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Fri, 28 Sep 2007 10:28:03 +0000 (10:28 -0000)
of setitimer in the tree).

static.thousand                                       16       -     -16
read_big_block                                        81      46     -35
getitimer                                             41       -     -41
setitimer                                             47       -     -47
__GI_setitimer                                        47       -     -47
do_time                                              480     386     -94
------------------------------------------------------------------------------
(add/remove: 0/4 grow/shrink: 0/2 up/down: 0/-280)           Total: -280 bytes

M    miscutils/hdparm.c

miscutils/hdparm.c

index a3a5a73..8a7204f 100644 (file)
@@ -429,10 +429,8 @@ static const char *const secu_str[] = {
 #define SIG                     0x00ff  /* signature location */
 #define SIG_VAL                 0x00a5  /* signature value */
 
-#define TIMING_MB               64
 #define TIMING_BUF_MB           1
 #define TIMING_BUF_BYTES        (TIMING_BUF_MB * 1024 * 1024)
-#define BUFCACHE_FACTOR         2
 
 #undef DO_FLUSHCACHE            /* under construction: force cache flush on -W0 */
 
@@ -505,6 +503,9 @@ struct globals {
        unsigned long hwif_ctrl;
        unsigned long hwif_irq;
 #endif
+#ifdef DO_FLUSHCACHE
+       unsigned char flushcache[4] = { WIN_FLUSHCACHE, 0, 0, 0 };
+#endif
 };
 #define G (*(struct globals*)&bb_common_bufsiz1)
 struct BUG_G_too_big {
@@ -1342,22 +1343,17 @@ static void seek_to_zero(/*int fd*/ void)
        xlseek(fd, (off_t) 0, SEEK_SET);
 }
 
-static int read_big_block(/*int fd,*/ char *buf)
+static void read_big_block(/*int fd,*/ char *buf)
 {
        int i;
 
-       i = read(fd, buf, TIMING_BUF_BYTES);
-       if (i != TIMING_BUF_BYTES) {
-               bb_error_msg("read(%d bytes) failed (rc=%d)", TIMING_BUF_BYTES, i);
-               return 1;
-       }
+       xread(fd, buf, TIMING_BUF_BYTES);
        /* access all sectors of buf to ensure the read fully completed */
        for (i = 0; i < TIMING_BUF_BYTES; i += 512)
                buf[i] &= 1;
-       return 0;
 }
 
-static unsigned long long do_blkgetsize(/*int fd*/ void)
+static unsigned long long dev_size_mb(/*int fd*/ void)
 {
        union {
                unsigned long long blksize64;
@@ -1365,85 +1361,88 @@ static unsigned long long do_blkgetsize(/*int fd*/ void)
        } u;
 
        if (0 == ioctl(fd, BLKGETSIZE64, &u.blksize64)) { // returns bytes
-               return u.blksize64 / 512;
+               return u.blksize64 / (1024 * 1024);
        }
        xioctl(fd, BLKGETSIZE, &u.blksize32); // returns sectors
-       return u.blksize32;
+       return u.blksize32 / (2 * 1024);
 }
 
-static void print_timing(unsigned t, double e)
+static void print_timing(unsigned long m, unsigned elapsed_us)
 {
-       if (t >= e)  /* more than 1MB/s */
-               printf("%4d MB in %.2f seconds = %.2f %cB/sec\n", t, e, t / e, 'M');
-       else
-               printf("%4d MB in %.2f seconds = %.2f %cB/sec\n", t, e, t / e * 1024, 'k');
+       unsigned sec = elapsed_us / 1000000;
+       unsigned hs = (elapsed_us % 1000000) / 10000;
+
+       printf("%5lu MB in %u.%02u seconds = %lu kB/s\n",
+               m, sec, hs,
+               /* Trying to not overflow 32-bit arith in m * CONST
+                * by keeping CONST not so big. + 1 prevents div-by-0. */
+               (m * (1024 * 1000000 / (64*1024))) / (elapsed_us / (64*1024) + 1)
+               // ~= (m * 1024 * 1000000) / elapsed_ms
+               // = (m * 1024) / (elapsed_ms / 1000000)
+               // = kb / elapsed_sec
+       );
 }
 
-static void do_time(int flag /*,int fd*/)
-/* flag = 0 time_cache, 1 time_device */
+static void do_time(int cache /*,int fd*/)
+/* cache=1: time cache: repeatedly read N MB at offset 0
+ * cache=0: time device: linear read, starting at offset 0
+ */
 {
-       static const struct itimerval thousand = {{1000, 0}, {1000, 0}};
-
-       struct itimerval itv;
+       unsigned max_iterations, iterations;
+       unsigned start; /* don't need to be long long */
        unsigned elapsed, elapsed2;
-       unsigned max_iterations, total_MB, iterations;
+       unsigned long total_MB;
        char *buf = xmalloc(TIMING_BUF_BYTES);
 
-       if (mlock(buf, TIMING_BUF_BYTES)) {
-               bb_perror_msg("mlock");
-               goto quit2;
-       }
+       if (mlock(buf, TIMING_BUF_BYTES))
+               bb_perror_msg_and_die("mlock");
 
-       max_iterations = do_blkgetsize() / (2 * 1024) / TIMING_BUF_MB;
+       /* Don't want to read past the end! */
+       max_iterations = UINT_MAX;
+       if (!cache)
+               max_iterations = dev_size_mb() / TIMING_BUF_MB;
 
-       /* Clear out the device request queues & give them time to complete */
+       /* Clear out the device request queues & give them time to complete.
+        * NB: *small* delay. User is expected to have a clue and to not run
+        * heavy io in parallel with measurements. */
        sync();
-       sleep(2);
-       if (flag == 0) { /* Time cache */
+       sleep(1);
+       if (cache) { /* Time cache */
                seek_to_zero();
-               if (read_big_block(buf))
-                       goto quit;
-               printf(" Timing buffer-cache reads:  ");
+               read_big_block(buf);
+               printf("Timing buffer-cache reads: ");
        } else { /* Time device */
-               printf(" Timing buffered disk reads: ");
+               printf("Timing buffered disk reads:");
        }
        fflush(stdout);
-       iterations = 0;
-       /*
-        * getitimer() is used rather than gettimeofday() because
-        * it is much more consistent (on my machine, at least).
-        */
-//TODO: get rid of
-       setitimer(ITIMER_REAL, &thousand, NULL);
+
        /* Now do the timing */
+       iterations = 0;
+       start = monotonic_us();
        do {
-               ++iterations;
-               if (flag == 0)
+               if (cache)
                        seek_to_zero();
-               if (read_big_block(buf))
-                       goto quit;
-               getitimer(ITIMER_REAL, &itv);
-               elapsed = (1000 - itv.it_value.tv_sec) * 1000000
-                               - itv.it_value.tv_usec;
+               read_big_block(buf);
+               elapsed = (unsigned)monotonic_us() - start;
+               ++iterations;
        } while (elapsed < 3000000 && iterations < max_iterations);
-       total_MB = iterations * TIMING_BUF_MB;
-       if (flag == 0) {
-               /* Now remove the lseek() and getitimer() overheads from the elapsed time */
-               setitimer(ITIMER_REAL, &thousand, NULL);
+       total_MB = (unsigned long)iterations * TIMING_BUF_MB;
+       //printf(" elapsed:%u iterations:%u ", elapsed, iterations);
+       if (cache) {
+               /* Now remove the lseek() and monotonic_us() overheads
+                * from elapsed */
+               start = monotonic_us();
                do {
                        seek_to_zero();
-                       getitimer(ITIMER_REAL, &itv);
-                       elapsed2 = (1000 - itv.it_value.tv_sec) * 1000000
-                                       - itv.it_value.tv_usec;
+                       elapsed2 = (unsigned)monotonic_us() - start;
                } while (--iterations);
+               //printf(" elapsed2:%u ", elapsed2);
                elapsed -= elapsed2;
-               total_MB *= BUFCACHE_FACTOR;
+               total_MB *= 2; // BUFCACHE_FACTOR (why?)
                flush_buffer_cache();
        }
-       print_timing(total_MB, elapsed / 1000000.0);
- quit:
+       print_timing(total_MB, elapsed);
        munlock(buf, TIMING_BUF_BYTES);
- quit2:
        free(buf);
 }
 
@@ -1688,7 +1687,6 @@ static void process_dev(char *devname)
 #ifndef WIN_FLUSHCACHE
 #define WIN_FLUSHCACHE 0xe7
 #endif
-               static unsigned char flushcache[4] = { WIN_FLUSHCACHE, 0, 0, 0 };
 #endif /* DO_FLUSHCACHE */
                args[2] = wcache ? 0x02 : 0x82;
                print_flag_on_off(get_wcache, "drive write-caching", wcache);
@@ -1749,7 +1747,7 @@ static void process_dev(char *devname)
                char buf[512];
                flush_buffer_cache();
                if (-1 == read(fd, buf, sizeof(buf)))
-                       bb_perror_msg("read(%d bytes) failed (rc=%d)", sizeof(buf), -1);
+                       bb_perror_msg("read(%d bytes) failed (rc=-1)", sizeof(buf));
        }
 #endif /* HDIO_DRIVE_CMD */
 
@@ -1912,9 +1910,9 @@ static void process_dev(char *devname)
                ioctl_or_warn(fd, BLKRRPART, NULL);
 
        if (do_ctimings)
-               do_time(0 /*,fd*/); /* time cache */
+               do_time(1 /*,fd*/); /* time cache */
        if (do_timings)
-               do_time(1 /*,fd*/); /* time device */
+               do_time(0 /*,fd*/); /* time device */
        if (do_flush)
                flush_buffer_cache();
        close(fd);