home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / dos / benchmar / fsx / fsx.c
Encoding:
C/C++ Source or Header  |  1990-05-18  |  16.4 KB  |  537 lines

  1.  
  2. /*
  3.  *
  4.  * fsx is a File System eXerciser.  It is a benchmark that attempts to
  5.  * quantify the performance of several filesystem operations that have been
  6.  * observed to be bottlenecks in I/O-intensive applications, specifically
  7.  * the text database work done in connection with the New Oxford English
  8.  * Dictionary Project at the University of Waterloo.
  9.  * 
  10.  * It performs a series of tests on a file of known size.  By default, that
  11.  * size is 50 Mb.  For each test, fsx reports the bytes processed per
  12.  * elapsed second, per CPU second, and the % CPU usage (user and system).
  13.  * 
  14.  * In each case, some care is taken to keep optimizers, no matter how
  15.  * clever, from noticing it's all bogus.  The idea is to make sure that
  16.  * these are real transfers from user space to the physical disk.  The
  17.  * tests are:
  18.  * 
  19.  * 1. Sequential Output
  20.  * 
  21.  * 1.1 Per-Character.  The file is written using the putc() stdio macro.
  22.  * The loop that does the writing should be small enough to fit into any
  23.  * reasonable I-cache.  The CPU exercised here is that required to do the
  24.  * stdio code plus the OS file space allocation.
  25.  * 
  26.  * 1.2 Block.  The file is created using write(2).  The CPU consumption
  27.  * should be just the OS file space allocation.
  28.  * 
  29.  * 1.3 Rewrite.  Each BUFSIZ of the file is read with read(2), dirtied, and
  30.  * rewritten with write(2), requiring an lseek(2).  Since no space
  31.  * allocation is done, and the I/O is well-localized, this should test the
  32.  * effectiveness of the filesystem cache and the speed of data transfer.
  33.  * 
  34.  * 2. Sequential Input
  35.  * 
  36.  * 2.1 Per-Character.  The file is read using the getc() stdio macro.  Once
  37.  * again, the inner loop is small.  This should exercise only stdio and
  38.  * sequential input.
  39.  * 
  40.  * 2.2 Block.  The file is read using read(2).  This should be a very pure
  41.  * test of sequential input performance.
  42.  * 
  43.  * 3. Random Seeks
  44.  * 
  45.  * This test seeks 1000 times to locations in the file specified by
  46.  * random() in bsd systems, drand48() on sysV systems.  In each case, the
  47.  * block is read with read(2).  In 10% of cases, it is dirtied and written
  48.  * back with write(2).
  49.  * 
  50.  * AXIOM: For any unix filesystem, the effective number of lseek(2) calls
  51.  * per second declines asymptotically to near 30, once the effect of
  52.  * caching is defeated.
  53.  * 
  54.  * The size of the file has a strong nonlinear effect on the results of
  55.  * this test.  Many Unix systems that have the memory available will make
  56.  * aggressive efforts to cache the whole thing, and report random I/O rates
  57.  * in the thousands per second, which is ridiculous.  As an extreme
  58.  * example, an IBM RISC 6000 with 64 Mb of memory reported 3,722 per second
  59.  * on a 50 Mb file.
  60.  *
  61.  * COPYRIGHT NOTICE: 
  62.  * Copyright (c) Tim Bray, 1990.
  63.  * Everybody is hereby granted rights to use, copy, and modify this program, 
  64.  *  provided only that the copyright notice above and the disclaimer below
  65.  *  are preserved without change.
  66.  * DISCLAIMER:
  67.  * This program is provided AS IS with no warranty of any kind, and
  68.  * The author makes no representation with respect to the adequacy of this
  69.  *  program for any particular purpose or with respect to its adequacy to 
  70.  *  produce any particular result, and
  71.  * The author shall not be liable for loss or damage arising out of
  72.  *  the use of this program regardless of how sustained, and
  73.  * In no event shall the author be liable for special, direct, indirect
  74.  *  or consequential damage, loss, costs or fees or expenses of any
  75.  *  nature or kind.
  76.  */
  77.  
  78. #include <stdio.h>
  79. #include <fcntl.h>
  80. #include <sys/types.h>
  81. #include <sys/time.h>
  82. #ifdef i386
  83. #include <limits.h>
  84. #include <sys/times.h>
  85. #else
  86. #include <sys/resource.h>
  87. #endif
  88.  
  89. #define IntSize (4)
  90. #define Elapsed (0)
  91. #define CPU (1)
  92. #define Searches (1000)
  93. #define UpdateSeek (10)
  94.  
  95. static double cpu_so_far();
  96. static void   doseek();
  97. static void   get_delta_t();
  98. static void   io_error();
  99. static void   newfile();
  100. static void   report();
  101. static double time_so_far();
  102. static void   timestamp();
  103. static void   usage();
  104. #ifdef i386
  105. static long   random();
  106. #endif
  107.  
  108. typedef enum
  109. {
  110.   Putc,
  111.   ReWrite,
  112.   FastWrite,
  113.   Getc,
  114.   FastRead,
  115.   Binary,
  116.   TestCount
  117. } tests_t;
  118.  
  119. static int    basetime;
  120. static double delta[(int) TestCount][2];
  121. static char * myname;
  122. static double last_cpustamp = 0.0;
  123. static double last_timestamp = 0.0;
  124. static int    lseek_count = 0;
  125.  
  126. main(argc, argv)
  127.   int    argc;
  128.   char * argv[];
  129. {
  130.   int    buf[BUFSIZ / IntSize];
  131.   int    bufindex;
  132.   int    chars[256];
  133.   char * dir;
  134.   int    fd;
  135.   char   name[BUFSIZ];
  136.   int    next;
  137.   int    size;
  138.   FILE * stream;
  139.   int    words;
  140.  
  141.   myname = argv[0];
  142.   fd = -1;
  143.   basetime = (int) time((time_t *) NULL);
  144.   size = 50;
  145.   dir = ".";
  146.  
  147.   for (next = 1; next < argc - 1; next++)
  148.     if (argv[next][0] == '-')
  149.     { /* option? */
  150.       if (strcmp(argv[next] + 1, "d") == 0)
  151.     dir = argv[next + 1];
  152.       else if (strcmp(argv[next] + 1, "s") == 0)
  153.     size = atoi(argv[next + 1]);
  154.       else
  155.     usage();
  156.       next++;
  157.     } /* option? */
  158.     else
  159.       usage();
  160.  
  161.   if (size < 1)
  162.     usage();
  163.   size *= (1024 * 1024);
  164.   sprintf(name, "%s/fsx.%d", dir, getpid());
  165.   printf("File '%s', size: %d\n", name, size);
  166.   fflush(stdout);
  167.  
  168.   /*
  169.    * Fill up a file, writing it a char at a time with the stdio putc()
  170.    *  facility.  This models the activity of a negligible-CPU filter
  171.    *  and exercises kernel file space allocation.  Should be measured with
  172.    *  disks of varying degrees of fullness.
  173.    */
  174.   printf("Writing with putc()...");
  175.   newfile(name, &fd, &stream, 1);
  176.   fflush(stdout);
  177.   timestamp();
  178.   for (words = 0; words < size; words++)
  179.     if (putc(words & 0x7f, stream) == EOF)
  180.       io_error("putc");
  181.   
  182.   /*
  183.    * note that we always close the file before measuring time, in an
  184.    *  effort to force as much of the I/O out as we can
  185.    */
  186.   if (fclose(stream) == -1)
  187.     io_error("fclose after putc");
  188.   get_delta_t((int) Putc);
  189.   printf("done\n");
  190.   fflush(stdout);
  191.  
  192.   /*
  193.    * Now read & rewrite it, a block at a time.  Change one word in each block
  194.    *  in case somebody's being clever about rewritten dirty blocks.
  195.    * This exercises sequential read-write performance and hence the cache,
  196.    *  but avoids any space-allocation work.
  197.    */
  198.   newfile(name, &fd, &stream, 0);
  199.   if (lseek(fd, (off_t) 0, 0) == (off_t) -1)
  200.     io_error("lseek(2) before rewrite");
  201.   printf("Rewriting...");
  202.   fflush(stdout);
  203.   timestamp();
  204.   bufindex = 0;
  205.   do
  206.   { /* while we can read a block */
  207.     if ((words = read(fd, (char *) buf, BUFSIZ)) == -1)
  208.       io_error("read(2)");
  209.     if (bufindex == words / IntSize)
  210.       bufindex = 0;
  211.     buf[bufindex++]++;
  212.     if (lseek(fd, (off_t) -words, 1) == -1)
  213.       io_error("relative lseek(2)");
  214.     if (write(fd, (char *) buf, words) == -1)
  215.       io_error("re write(2)");
  216.   } /* while we can read a block */
  217.   while (words == BUFSIZ);
  218.   if (close(fd) == -1)
  219.     io_error("close after rewrite");
  220.   get_delta_t((int) ReWrite);
  221.   printf("done\n");
  222.   fflush(stdout);
  223.  
  224.   /*
  225.    * Now rewrite the whole file from scratch, but a block at a time rather
  226.    *  than with stdio.  Exercises space allocation a little more stringently,
  227.    *  and by comparison with the putc test, quantifies stdio overhead a bit.
  228.    * Once again, dirty each block.
  229.    */
  230.   newfile(name, &fd, &stream, 1);
  231.   printf("Writing intelligently...");
  232.   for (words = 0; words < BUFSIZ / IntSize; words++)
  233.     buf[words] = 0;
  234.   fflush(stdout);
  235.   timestamp();
  236.   for (words = bufindex = 0; words < (size / BUFSIZ); words++)
  237.   { /* for each word */
  238.     if (bufindex == (BUFSIZ/IntSize))
  239.       bufindex = 0;
  240.     buf[bufindex++]++;
  241.     if (write(fd, (char *) buf, BUFSIZ) == -1)
  242.       io_error("write(2)");
  243.   } /* for each word */
  244.   if (close(fd) == -1)
  245.     io_error("close after fast write");
  246.   get_delta_t((int) FastWrite);
  247.   printf("done\n");
  248.   fflush(stdout);
  249.  
  250.   /*
  251.    * Now read them all back with getc, excercising default character-
  252.    *  at-a-time input.  Do a character frequency count just to fool
  253.    *  any optimizers that may notice that they're not being used.
  254.    */
  255.   newfile(name, &fd, &stream, 0);
  256.   for (words = 0; words < 256; words++)
  257.     chars[words] = 0;
  258.   printf("Reading with getc()...");
  259.   fflush(stdout);
  260.   timestamp();
  261.   for (words = 0; words < size; words++)
  262.   { /* for each byte */
  263.     if ((next = getc(stream)) == EOF)
  264.       io_error("getc(3)");
  265.     chars[next]++;
  266.   } /* for each byte */
  267.   if (fclose(stream) == -1)
  268.     io_error("fclose after getc");
  269.   get_delta_t((int) Getc);
  270.   printf("done\n");
  271.   fflush(stdout);
  272.  
  273.   /* use the frequency count */
  274.   for (words = 0; words < 256; words++)
  275.     sprintf((char *) buf, "%d", chars[words]);
  276.  
  277.   /*
  278.    * Now suck it in, BUFSIZ at a time, as fast as we can.
  279.    */
  280.   newfile(name, &fd, &stream, 0);
  281.   if (lseek(fd, 0L, 0) == -1)
  282.     io_error("lseek before read");
  283.   printf("Reading intelligently...");
  284.   fflush(stdout);
  285.   timestamp();
  286.   do
  287.     if ((words = read(fd, (char *) buf, BUFSIZ)) == -1)
  288.       io_error("read(2)");
  289.   while (words);
  290.   if (close(fd) == -1)
  291.     io_error("close after read");
  292.   get_delta_t((int) FastRead);
  293.   printf("done\n");
  294.   fflush(stdout);
  295.  
  296.   /*
  297.    * Now do some random I/O.  Originally these were binary searches, but
  298.    *  that is well-behaved, since the first few seeks are always the same.
  299.    *  Probably an application that was doing this kind of thing would keep
  300.    *  its own cache of the top few levels of the tree or whatever - we're
  301.    *  just interested in 'How much random I/O can be done?'
  302.    */
  303.   newfile(name, &fd, &stream, 0);
  304.   timestamp();
  305.   printf("Seeking...");
  306.   fflush(stdout);
  307.   for (lseek_count = 0; lseek_count < Searches; lseek_count++)
  308.     doseek(random() % size, fd, ((lseek_count % UpdateSeek) == 0));
  309.   if (close(fd) == -1)
  310.     io_error("close after read");
  311.   get_delta_t((int) Binary);
  312.   printf("done\n");
  313.   fflush(stdout);
  314.  
  315.   report(size);
  316.   unlink(name);
  317. }
  318.  
  319. static void
  320. report(size)
  321.   int   size;
  322. {
  323.   printf("\n");
  324.   printf("Times reported are elapsed / cpu / %%cpu usage.\n");
  325.  
  326.   printf("\nSequential output\n");
  327.   printf("putc() bytes/sec: %d / %d / %.1f%%\n",
  328.     (int) (((double) size) / delta[(int) Putc][Elapsed]),
  329.     (int) (((double) size) / delta[(int) Putc][CPU]),
  330.     delta[(int) Putc][CPU] / delta[(int) Putc][Elapsed] * 100.0);
  331.   printf("write() bytes/sec: %d / %d / %.1f%%\n",
  332.     (int) (((double) size) / delta[(int) FastWrite][Elapsed]),
  333.     (int) (((double) size) / delta[(int) FastWrite][CPU]),
  334.     delta[(int) FastWrite][CPU] / delta[(int) FastWrite][Elapsed] * 100.0);
  335.   printf("putc() multiplier: %.1f / %.1f\n",
  336.     delta[(int) Putc][Elapsed] / delta[(int) FastWrite][Elapsed],
  337.     delta[(int) Putc][CPU] / delta[(int) FastWrite][CPU]);
  338.   printf("Sequential output time: %.1f / %.1f / %.1f%%\n\n",
  339.     delta[(int) FastWrite][Elapsed] + delta[(int) Putc][Elapsed],
  340.     delta[(int) FastWrite][CPU] + delta[(int) Putc][CPU],
  341.     (delta[(int) FastWrite][CPU] + delta[(int) Putc][CPU])
  342.     / (delta[(int) FastWrite][Elapsed] + delta[(int) Putc][Elapsed])
  343.     * 100.0);
  344.  
  345.   printf("\nSequential input\n");
  346.   printf("getc() bytes/sec: %d / %d / %.1f%%\n",
  347.     (int) (((double) size) / delta[(int) Getc][Elapsed]),
  348.     (int) (((double) size) / delta[(int) Getc][CPU]),
  349.     delta[(int) Getc][CPU] / delta[(int) Getc][Elapsed] * 100.0);
  350.   printf("read() bytes/sec: %d / %d / %.1f%%\n",
  351.     (int) (((double) size) / delta[(int) FastRead][Elapsed]),
  352.     (int) (((double) size) / delta[(int) FastRead][CPU]),
  353.     delta[(int) FastRead][CPU] / delta[(int) FastRead][Elapsed] * 100.0);
  354.   printf("getc() multiplier: %.1f / %.1f\n",
  355.     delta[(int) Getc][Elapsed] / delta[(int) FastRead][Elapsed],
  356.     delta[(int) Getc][CPU] / delta[(int) FastRead][CPU]);
  357.   printf("Sequential input time: %.1f / %.1f / %.1f%%\n\n",
  358.     delta[(int) Getc][Elapsed] + delta[(int) FastRead][Elapsed],
  359.     delta[(int) Getc][CPU] + delta[(int) FastRead][CPU],
  360.     (delta[(int) Getc][CPU] + delta[(int) FastRead][CPU])
  361.     / (delta[(int) Getc][Elapsed] + delta[(int) FastRead][Elapsed])
  362.     * 100.0);
  363.  
  364.   printf("\nSequential rewrite\n");
  365.   printf("Sequential rewrite bytes/sec: %d / %d / %.1f%%\n",
  366.     (int) (((double) size) / delta[(int) ReWrite][Elapsed]),
  367.     (int) (((double) size) / delta[(int) ReWrite][CPU]),
  368.     delta[(int) ReWrite][CPU] / delta[(int) ReWrite][Elapsed] * 100.0);
  369.   printf("Sequential rewrite time: %.1f / %.1f / %.1f%%\n",
  370.     delta[(int) ReWrite][Elapsed], delta[(int) ReWrite][CPU],
  371.     delta[(int) ReWrite][CPU] / delta[(int) ReWrite][Elapsed] * 100.0);
  372.  
  373.   printf("\nRandom I/O\n");
  374.   printf("Random reads/sec: %.1f / %.1f / %.1f%%\n",
  375.     ((double) lseek_count) / delta[(int) Binary][Elapsed],
  376.     ((double) lseek_count) / delta[(int) Binary][CPU],
  377.     delta[(int) Binary][CPU] / delta[(int) Binary][Elapsed] * 100.0);
  378. }
  379.  
  380. static void
  381. newfile(name, fd, stream, create)
  382.   char *   name;
  383.   int *    fd;
  384.   FILE * * stream;
  385.   int      create;
  386. {
  387.   if (create)
  388.   { /* create from scratch */
  389.     if (unlink(name) == -1 && *fd != -1)
  390.       io_error("unlink");
  391.     *fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0777);
  392.   } /* create from scratch */
  393.   else
  394.     *fd = open(name, O_RDWR, 0777);
  395.  
  396.   if (*fd == -1)
  397.     io_error(name);
  398.   *stream = fdopen(*fd, "r+");
  399.   if (*stream == NULL)
  400.     io_error("fdopen");
  401. }
  402.  
  403. static void
  404. usage()
  405. {
  406.   fprintf(stderr, "usage: %s [-d scratch-directory] [-s size-in-megabytes]\n",
  407.     myname);
  408.   exit(1);
  409. }
  410.  
  411. static void
  412. timestamp()
  413. {
  414.   last_timestamp = time_so_far();
  415.   last_cpustamp = cpu_so_far();
  416. }
  417.  
  418. static void 
  419. get_delta_t(which)
  420.   int which;
  421. {
  422.   delta[which][Elapsed] = time_so_far() - last_timestamp;
  423.   delta[which][CPU] = cpu_so_far() - last_cpustamp;
  424. }
  425.  
  426. static double 
  427. cpu_so_far()
  428. {
  429. #ifdef i386
  430.   struct tms tms;
  431.  
  432.   if (times(&tms) == -1)
  433.     io_error("times");
  434.   return ((double) tms.tms_utime) / ((double) CLK_TCK) +
  435.     ((double) tms.tms_stime) / ((double) CLK_TCK);
  436.  
  437. #else
  438.   struct rusage rusage;
  439.  
  440.   getrusage(RUSAGE_SELF, &rusage);
  441.   return
  442.     ((double) rusage.ru_utime.tv_sec) +
  443.       (((double) rusage.ru_utime.tv_usec) / 1000000.0) +
  444.         ((double) rusage.ru_stime.tv_sec) +
  445.           (((double) rusage.ru_stime.tv_usec) / 1000000.0);
  446. #endif
  447. }
  448.  
  449. static double
  450. time_so_far()
  451. {
  452. #ifdef i386
  453.   int        val;
  454.   struct tms tms;
  455.  
  456.   if ((val = times(&tms)) == -1)
  457.     io_error("times");
  458.  
  459.   return ((double) val) / ((double) CLK_TCK);
  460.  
  461. #else
  462.   struct timeval tp;
  463.  
  464.   if (gettimeofday(&tp, (struct timezone *) NULL) == -1)
  465.     io_error("gettimeofday");
  466.   return ((double) (tp.tv_sec - basetime)) +
  467.     (((double) tp.tv_usec) / 1000000.0);
  468. #endif
  469. }
  470.  
  471. static void
  472. io_error(message)
  473.   char * message;
  474. {
  475.   char buf[BUFSIZ];
  476.  
  477.   sprintf(buf, "%s: drastic I/O error (%s)", myname, message);
  478.   perror(buf);
  479.   exit(1);
  480. }
  481.  
  482. /*
  483.  * Do a typical-of-something random I/O.  Any serious application that
  484.  *  has a random I/O bottleneck is going to be smart enough to operate
  485.  *  in a page mode, and not stupidly pull individual words out at
  486.  *  odd offsets.  To keep the cache from getting too clever, some
  487.  *  pages must be updated.  However an application that updated each of
  488.  *  many random pages that it looked at is hard to imagine.  
  489.  * However, it would be wrong to put the update percentage in as a
  490.  *  parameter - the effect is too nonlinear.  Need a profile
  491.  *  of what Oracle or Ingres or some such actually does.
  492.  * Be warned - there is a *sharp* elbow in this curve - on a 1-Mb file,
  493.  *  most substantial unix systems show >2000 random I/Os per second -
  494.  *  obviously they've cached the whole thing and are just doing buffer
  495.  *  copies.  
  496.  */
  497. static void 
  498. doseek(where, fd, update)
  499.   long where;
  500.   int  fd;
  501.   int  update;
  502. {
  503.   int  buf[BUFSIZ / IntSize];
  504.   long probe;
  505.   int  size;
  506.  
  507.   probe = (where / BUFSIZ) * BUFSIZ;
  508.   if (lseek(fd, probe, 0) != probe)
  509.     io_error("lseek in doseek");
  510.   if ((size = read(fd, (char *) buf, BUFSIZ)) == -1)
  511.     io_error("read in doseek");
  512.  
  513.   /* every so often, update a block */
  514.   if (update)
  515.   { /* update this block */
  516.  
  517.     /* touch a word */
  518.     buf[((int) random() % (size/IntSize - 2)) + 1]--;
  519.     if (lseek(fd, (long) probe, 0) != probe)
  520.       io_error("lseek in doseek update");
  521.     if (write(fd, (char *) buf, size) == -1)
  522.       io_error("write in doseek");
  523.   } /* update this block */
  524. }
  525.   
  526. #ifdef i386
  527. static char randseed[] = "ioioio";
  528.  
  529. static long
  530. random()
  531. {
  532.   return nrand48(randseed);
  533. }
  534. #endif
  535.  
  536.  
  537.