home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1328 < prev    next >
Encoding:
Internet Message Format  |  1990-12-28  |  22.3 KB

  1. From: tbray@watsol.waterloo.edu (Tim Bray)
  2. Newsgroups: alt.sources
  3. Subject: [comp.arch] Unix Filesystem Benchmark Results (long)
  4. Message-ID: <12022@stag.math.lsa.umich.edu>
  5. Date: 14 May 90 02:08:05 GMT
  6.  
  7. Archive-name: fsx/13-May-90
  8. Original-posting-by: tbray@watsol.waterloo.edu (Tim Bray)
  9. Original-subject: Unix Filesystem Benchmark Results (long)
  10. Reposted-by: emv@math.lsa.umich.edu (Edward Vielmetti)
  11.  
  12. [This is an experimental alt.sources re-posting from the newsgroup(s)
  13. comp.arch. Comments on this service to emv@math.lsa.umich.edu 
  14. (Edward Vielmetti).]
  15.  
  16.  
  17. Howdy folks.  We've been hacking text databases here on the New OED project
  18. for years, and have been disappointed at the performance of Unix filesystems
  19. in general, and also at the tools available for measuring that performance.
  20.  
  21. So I wrote a program to address this need.  It fails to meet the SPEC
  22. criterion of being real working code that does something useful, but it does
  23. produce very interesting numbers, most of which I believe.
  24.  
  25. This posting includes a summary of the results of running the benchmark on all
  26. the machines I could get my hands on, some subjective commentary, and the
  27. source code for the benchmark, the comments in which should be read by
  28. everyone before beginning to think about believing these numbers.
  29.  
  30. Cheers, Tim Bray, Open Text Systems, Waterloo, Ont.
  31.  
  32.            -------Sequential Output-------- ---Sequential Input-- --Random-
  33.            -Per Char- --Block--- -Rewrite-- -Per Char- --Block--- --Seeks--
  34.            K/sec %CPU K/sec %CPU K/sec %CPU K/sec %CPU K/sec %CPU /sec %CPU
  35.  
  36. DEC 5810     330 73.6   690 62.8   195 29.0   274 61.6   649 45.9 27.7  4.9
  37.              434 92.7   732 63.3   206 29.2   382 80.7   701 47.6 34.3  5.1
  38.              418 88.1   718 65.0   196 28.7   386 81.7   744 52.3 27.1  5.8
  39.  
  40. MipsM/2K     316 35.2   381  8.8   302 11.4   611 67.9  1369 27.4 28.5  1.2
  41.              345 38.1   435 10.0   226  8.8   399 44.1   876 17.3 21.3  1.3
  42.              362 40.0   440 10.5   371 15.0   785 89.1  1762 37.3 26.4  1.8
  43.  
  44. NeXT         243 92.0   352 48.8   210 38.9   217 95.0   782 72.4 28.4 12.7
  45.              243 92.9   360 50.0   216 40.7   251 95.8   773 71.4 28.7 14.3
  46.              245 93.1   346 48.2   215 40.3   218 95.7   777 71.3 26.7 13.7
  47.  
  48. Seq.Symm.    178 99.1   951 90.7   307 48.1   151 95.7   694 57.2 35.6  6.6
  49.              176 98.2   926 85.8   285 43.4   150 95.4   655 51.5 33.4  6.7
  50.  
  51. VAX 8650     197 81.7   262 22.3   116 14.1   255 81.5   434 16.6 22.1  2.5
  52.              186 77.7   214 18.6    99 12.7   144 46.9   361 15.1 16.8  2.2
  53.  
  54. Sun 4/260    298 83.2   791 58.2   296 26.3   312 94.5   840 44.8 ****
  55.              328 92.6   796 59.7   288 26.4   298 91.2   726 39.7 ****
  56.  
  57. 386          229 91.9   241 27.5   207 46.4   209 91.9   437 56.0 34.4  7.0
  58.  
  59. IBM R 6000   654 89.0  1178 22.6   434 13.1   750 94.2  1539 20.9 39.6  3.0
  60.              712 96.5  1258 24.1   444 13.4   727 91.3  1546 21.7 33.7  2.3
  61.  
  62. Commentary: feel free to disbelieve
  63.  
  64. DEC 5810: Ultrix 3.1C-0 (Rev 42), 1.2G filesystem on disk, 91% full.
  65.  The R2000 in here is slower than the R3000 in the Mips M/2K, but the
  66.  I/O rates are competitive; however this box has to burn more CPU to
  67.  achieve those rates.  Note that block input and output are about the
  68.  same speed, making it slower than the MIPS on input but faster on
  69.  output.  
  70.  
  71. Mips M/2000: UMIPS 4.0, 564M filesystem on MIPS disk, 83% full.
  72.  This is a very fast CPU, and it shows in the %CPU columns.  The
  73.  block-mode input number is very good - over 1.25 effective
  74.  megabytes/sec off the disk.  Note the block-in vs. block-out anomaly 
  75.  with respect to the DEC machine.  Also, the seeks/sec. are unexciting.
  76.  
  77. NeXT: SW Version 1.0, 446M filesystem on NeXT HD, 82% full.
  78.  One of the slower CPUs in the crowd, and it shows.  Given its price
  79.  relative to some of these other boxes, this is excellent performance;
  80.  the NeXT is supposed to have fancy DMAs.  Didn't have an optical I
  81.  could write on, but that would be very interesting.
  82.  
  83. Sequent: 10 386 processors, Dynix 3.0.17.9, 564M filesystem, 82% full.
  84.  The individual processors in here are fairly-slow 386es, and that shows
  85.  in the %CPU column.  But burning 100% of a 386 to do the filesystem and
  86.  leaving the other 9 to do useful work seems smart to me.  The block I/O
  87.  throughput and in particular the seeks/second are very good.  Looks
  88.  like a good database machine. 
  89.  
  90. VAX 8650: 4.3bsd, 584M filesystem on CDC Wren VI on UDA, 33% full.
  91.  Well, the myth of VAX as good I/O machine seems to be biting the dust
  92.  here.  An 8650 is a pretty fast CPU, and has an elaborate I/O
  93.  architecture.  There must be a bottleneck here - candidates are the
  94.  UDA, the Unibus, and 4.3bsd.  Note the %CPU figures are very
  95.  low, which may just be a result of the I/O being so slow, or some CISC
  96.  artifact.  It's even slow at seeking.  Seven years ago, we used to have
  97.  some 780s with RP07's on MASSBUSes - I wouldn't be surprised if they
  98.  measured faster than this.
  99.  
  100. Sun 4/260: SunOS 4.0.3, 526M filesystem on Fuji M2344, 87% full.
  101.  Not bad, consistently in the top half.  Some of the speed seems to be 
  102.  purchased by burning CPU - the %CPU figures are quite high.  Couldn't
  103.  get meaningful numbers on seeks/sec because we don't have the disk
  104.  space to spare.  With 32M of memory, operating on a 50M file, the
  105.  caching drove the seeks/sec up over 60.
  106.  
  107. 386: 20 MHz AMI motherboard, 386/ix V2.0.2, 180M filesys on MaxStor ESDI
  108.      disk, 51% full.
  109.  I think this is pretty damn good performance for a machine which is
  110.  an order of magnitude less in cost than some of the other boxes here.
  111.  In particular, its random I/O performance seems to rival that of the
  112.  big RISC chips, both the DEC and MIPS implementations (8M memory for a
  113.  50M file, so it ain't caching much).  The scuttlebutt says that these
  114.  can be made still faster using caching controllers. 
  115.  
  116. IBM R6000, Beta Test AIX, 450M filesystem on "Cheap SCSI", 34% full.
  117.  Was never able to run the tests on an idle system, something was
  118.  burning CPU all the time, if vmstat(1) is to be believed, but I
  119.  couldn't find a candidate using ps(1) - except perhaps something called
  120.  "kproc", but that sounds like a fake kernel task.  Also, there was only
  121.  about 50 Mb free on the filesystem, so the sysadmin did something with
  122.  "logical volumes" and the "chfs(1)" command then the filesys was 250M
  123.  bigger - might this be muddying the waters?  A test run before the chfs
  124.  showed *unbelievable* rates, like 5+ Mb system coming off the filesys
  125.  using read(2).  Anyhow, the conclusion is  clear - this puppy is an 
  126.  I/O monster.
  127.  
  128. --cut here---------------------------------------------------------------
  129.  
  130. /*
  131.  *
  132.  * fsx is a File System eXerciser.  It is a benchmark that attempts to
  133.  * quantify the performance of several filesystem operations that have been
  134.  * observed to be bottlenecks in I/O-intensive applications, specifically
  135.  * the text database work done in connection with the New Oxford English
  136.  * Dictionary Project at the University of Waterloo.
  137.  * 
  138.  * It performs a series of tests on a file of known size.  By default, that
  139.  * size is 50 Mb.  For each test, fsx reports the bytes processed per
  140.  * elapsed second, per CPU second, and the % CPU usage (user and system).
  141.  * 
  142.  * In each case, some care is taken to keep optimizers, no matter how
  143.  * clever, from noticing it's all bogus.  The idea is to make sure that
  144.  * these are real transfers from user space to the physical disk.  The
  145.  * tests are:
  146.  * 
  147.  * 1. Sequential Output
  148.  * 
  149.  * 1.1 Per-Character.  The file is written using the putc() stdio macro.
  150.  * The loop that does the writing should be small enough to fit into any
  151.  * reasonable I-cache.  The CPU exercised here is that required to do the
  152.  * stdio code plus the OS file space allocation.
  153.  * 
  154.  * 1.2 Block.  The file is created using write(2).  The CPU consumption
  155.  * should be just the OS file space allocation.
  156.  * 
  157.  * 1.3 Rewrite.  Each BUFSIZ of the file is read with read(2), dirtied, and
  158.  * rewritten with write(2), requiring an lseek(2).  Since no space
  159.  * allocation is done, and the I/O is well-localized, this should test the
  160.  * effectiveness of the filesystem cache and the speed of data transfer.
  161.  * 
  162.  * 2. Sequential Input
  163.  * 
  164.  * 2.1 Per-Character.  The file is read using the getc() stdio macro.  Once
  165.  * again, the inner loop is small.  This should exercise only stdio and
  166.  * sequential input.
  167.  * 
  168.  * 2.2 Block.  The file is read using read(2).  This should be a very pure
  169.  * test of sequential input performance.
  170.  * 
  171.  * 3. Random Seeks
  172.  * 
  173.  * This test seeks 1000 times to locations in the file specified by
  174.  * random() in bsd systems, drand48() on sysV systems.  In each case, the
  175.  * block is read with read(2).  In 10% of cases, it is dirtied and written
  176.  * back with write(2).
  177.  * 
  178.  * AXIOM: For any unix filesystem, the effective number of lseek(2) calls
  179.  * per second declines asymptotically to near 30, once the effect of
  180.  * caching is defeated.
  181.  * 
  182.  * The size of the file has a strong nonlinear effect on the results of
  183.  * this test.  Many Unix systems that have the memory available will make
  184.  * aggressive efforts to cache the whole thing, and report random I/O rates
  185.  * in the thousands per second, which is ridiculous.  As an extreme
  186.  * example, an IBM RISC 6000 with 64 Mb of memory reported 3,722 per second
  187.  * on a 50 Mb file.
  188.  *
  189.  * COPYRIGHT NOTICE: 
  190.  * Copyright (c) Tim Bray, 1990.
  191.  * Everybody is hereby granted rights to use, copy, and modify this program, 
  192.  *  provided only that the copyright notice above and the disclaimer below
  193.  *  are preserved without change.
  194.  * DISCLAIMER:
  195.  * This program is provided AS IS with no warranty of any kind, and
  196.  * The author makes no representation with respect to the adequacy of this
  197.  *  program for any particular purpose or with respect to its adequacy to 
  198.  *  produce any particular result, and
  199.  * The author shall not be liable for loss or damage arising out of
  200.  *  the use of this program regardless of how sustained, and
  201.  * In no event shall the author be liable for special, direct, indirect
  202.  *  or consequential damage, loss, costs or fees or expenses of any
  203.  *  nature or kind.
  204.  */
  205.  
  206. #include <stdio.h>
  207. #include <fcntl.h>
  208. #include <sys/types.h>
  209. #include <sys/time.h>
  210. #ifdef i386
  211. #include <limits.h>
  212. #include <sys/times.h>
  213. #else
  214. #include <sys/resource.h>
  215. #endif
  216.  
  217. #define IntSize (4)
  218. #define Elapsed (0)
  219. #define CPU (1)
  220. #define Searches (1000)
  221. #define UpdateSeek (10)
  222.  
  223. static double cpu_so_far();
  224. static void   doseek();
  225. static void   get_delta_t();
  226. static void   io_error();
  227. static void   newfile();
  228. static void   report();
  229. static double time_so_far();
  230. static void   timestamp();
  231. static void   usage();
  232. #ifdef i386
  233. static long   random();
  234. #endif
  235.  
  236. typedef enum
  237. {
  238.   Putc,
  239.   ReWrite,
  240.   FastWrite,
  241.   Getc,
  242.   FastRead,
  243.   Binary,
  244.   TestCount
  245. } tests_t;
  246.  
  247. static int    basetime;
  248. static double delta[(int) TestCount][2];
  249. static char * myname;
  250. static double last_cpustamp = 0.0;
  251. static double last_timestamp = 0.0;
  252. static int    lseek_count = 0;
  253.  
  254. main(argc, argv)
  255.   int    argc;
  256.   char * argv[];
  257. {
  258.   int    buf[BUFSIZ / IntSize];
  259.   int    bufindex;
  260.   int    chars[256];
  261.   char * dir;
  262.   int    fd;
  263.   char   name[BUFSIZ];
  264.   int    next;
  265.   int    size;
  266.   FILE * stream;
  267.   int    words;
  268.  
  269.   myname = argv[0];
  270.   fd = -1;
  271.   basetime = (int) time((time_t *) NULL);
  272.   size = 50;
  273.   dir = ".";
  274.  
  275.   for (next = 1; next < argc - 1; next++)
  276.     if (argv[next][0] == '-')
  277.     { /* option? */
  278.       if (strcmp(argv[next] + 1, "d") == 0)
  279.     dir = argv[next + 1];
  280.       else if (strcmp(argv[next] + 1, "s") == 0)
  281.     size = atoi(argv[next + 1]);
  282.       else
  283.     usage();
  284.       next++;
  285.     } /* option? */
  286.     else
  287.       usage();
  288.  
  289.   if (size < 1)
  290.     usage();
  291.   size *= (1024 * 1024);
  292.   sprintf(name, "%s/fsx.%d", dir, getpid());
  293.   printf("File '%s', size: %d\n", name, size);
  294.   fflush(stdout);
  295.  
  296.   /*
  297.    * Fill up a file, writing it a char at a time with the stdio putc()
  298.    *  facility.  This models the activity of a negligible-CPU filter
  299.    *  and exercises kernel file space allocation.  Should be measured with
  300.    *  disks of varying degrees of fullness.
  301.    */
  302.   printf("Writing with putc()...");
  303.   newfile(name, &fd, &stream, 1);
  304.   fflush(stdout);
  305.   timestamp();
  306.   for (words = 0; words < size; words++)
  307.     if (putc(words & 0x7f, stream) == EOF)
  308.       io_error("putc");
  309.   
  310.   /*
  311.    * note that we always close the file before measuring time, in an
  312.    *  effort to force as much of the I/O out as we can
  313.    */
  314.   if (fclose(stream) == -1)
  315.     io_error("fclose after putc");
  316.   get_delta_t((int) Putc);
  317.   printf("done\n");
  318.   fflush(stdout);
  319.  
  320.   /*
  321.    * Now read & rewrite it, a block at a time.  Change one word in each block
  322.    *  in case somebody's being clever about rewritten dirty blocks.
  323.    * This exercises sequential read-write performance and hence the cache,
  324.    *  but avoids any space-allocation work.
  325.    */
  326.   newfile(name, &fd, &stream, 0);
  327.   if (lseek(fd, (off_t) 0, 0) == (off_t) -1)
  328.     io_error("lseek(2) before rewrite");
  329.   printf("Rewriting...");
  330.   fflush(stdout);
  331.   timestamp();
  332.   bufindex = 0;
  333.   do
  334.   { /* while we can read a block */
  335.     if ((words = read(fd, (char *) buf, BUFSIZ)) == -1)
  336.       io_error("read(2)");
  337.     if (bufindex == words / IntSize)
  338.       bufindex = 0;
  339.     buf[bufindex++]++;
  340.     if (lseek(fd, (off_t) -words, 1) == -1)
  341.       io_error("relative lseek(2)");
  342.     if (write(fd, (char *) buf, words) == -1)
  343.       io_error("re write(2)");
  344.   } /* while we can read a block */
  345.   while (words == BUFSIZ);
  346.   if (close(fd) == -1)
  347.     io_error("close after rewrite");
  348.   get_delta_t((int) ReWrite);
  349.   printf("done\n");
  350.   fflush(stdout);
  351.  
  352.   /*
  353.    * Now rewrite the whole file from scratch, but a block at a time rather
  354.    *  than with stdio.  Exercises space allocation a little more stringently,
  355.    *  and by comparison with the putc test, quantifies stdio overhead a bit.
  356.    * Once again, dirty each block.
  357.    */
  358.   newfile(name, &fd, &stream, 1);
  359.   printf("Writing intelligently...");
  360.   for (words = 0; words < BUFSIZ / IntSize; words++)
  361.     buf[words] = 0;
  362.   fflush(stdout);
  363.   timestamp();
  364.   for (words = bufindex = 0; words < (size / BUFSIZ); words++)
  365.   { /* for each word */
  366.     if (bufindex == (BUFSIZ/IntSize))
  367.       bufindex = 0;
  368.     buf[bufindex++]++;
  369.     if (write(fd, (char *) buf, BUFSIZ) == -1)
  370.       io_error("write(2)");
  371.   } /* for each word */
  372.   if (close(fd) == -1)
  373.     io_error("close after fast write");
  374.   get_delta_t((int) FastWrite);
  375.   printf("done\n");
  376.   fflush(stdout);
  377.  
  378.   /*
  379.    * Now read them all back with getc, excercising default character-
  380.    *  at-a-time input.  Do a character frequency count just to fool
  381.    *  any optimizers that may notice that they're not being used.
  382.    */
  383.   newfile(name, &fd, &stream, 0);
  384.   for (words = 0; words < 256; words++)
  385.     chars[words] = 0;
  386.   printf("Reading with getc()...");
  387.   fflush(stdout);
  388.   timestamp();
  389.   for (words = 0; words < size; words++)
  390.   { /* for each byte */
  391.     if ((next = getc(stream)) == EOF)
  392.       io_error("getc(3)");
  393.     chars[next]++;
  394.   } /* for each byte */
  395.   if (fclose(stream) == -1)
  396.     io_error("fclose after getc");
  397.   get_delta_t((int) Getc);
  398.   printf("done\n");
  399.   fflush(stdout);
  400.  
  401.   /* use the frequency count */
  402.   for (words = 0; words < 256; words++)
  403.     sprintf((char *) buf, "%d", chars[words]);
  404.  
  405.   /*
  406.    * Now suck it in, BUFSIZ at a time, as fast as we can.
  407.    */
  408.   newfile(name, &fd, &stream, 0);
  409.   if (lseek(fd, 0L, 0) == -1)
  410.     io_error("lseek before read");
  411.   printf("Reading intelligently...");
  412.   fflush(stdout);
  413.   timestamp();
  414.   do
  415.     if ((words = read(fd, (char *) buf, BUFSIZ)) == -1)
  416.       io_error("read(2)");
  417.   while (words);
  418.   if (close(fd) == -1)
  419.     io_error("close after read");
  420.   get_delta_t((int) FastRead);
  421.   printf("done\n");
  422.   fflush(stdout);
  423.  
  424.   /*
  425.    * Now do some random I/O.  Originally these were binary searches, but
  426.    *  that is well-behaved, since the first few seeks are always the same.
  427.    *  Probably an application that was doing this kind of thing would keep
  428.    *  its own cache of the top few levels of the tree or whatever - we're
  429.    *  just interested in 'How much random I/O can be done?'
  430.    */
  431.   newfile(name, &fd, &stream, 0);
  432.   timestamp();
  433.   printf("Seeking...");
  434.   fflush(stdout);
  435.   for (lseek_count = 0; lseek_count < Searches; lseek_count++)
  436.     doseek(random() % size, fd, ((lseek_count % UpdateSeek) == 0));
  437.   if (close(fd) == -1)
  438.     io_error("close after read");
  439.   get_delta_t((int) Binary);
  440.   printf("done\n");
  441.   fflush(stdout);
  442.  
  443.   report(size);
  444.   unlink(name);
  445. }
  446.  
  447. static void
  448. report(size)
  449.   int   size;
  450. {
  451.   printf("\n");
  452.   printf("Times reported are elapsed / cpu / %%cpu usage.\n");
  453.  
  454.   printf("\nSequential output\n");
  455.   printf("putc() bytes/sec: %d / %d / %.1f%%\n",
  456.     (int) (((double) size) / delta[(int) Putc][Elapsed]),
  457.     (int) (((double) size) / delta[(int) Putc][CPU]),
  458.     delta[(int) Putc][CPU] / delta[(int) Putc][Elapsed] * 100.0);
  459.   printf("write() bytes/sec: %d / %d / %.1f%%\n",
  460.     (int) (((double) size) / delta[(int) FastWrite][Elapsed]),
  461.     (int) (((double) size) / delta[(int) FastWrite][CPU]),
  462.     delta[(int) FastWrite][CPU] / delta[(int) FastWrite][Elapsed] * 100.0);
  463.   printf("putc() multiplier: %.1f / %.1f\n",
  464.     delta[(int) Putc][Elapsed] / delta[(int) FastWrite][Elapsed],
  465.     delta[(int) Putc][CPU] / delta[(int) FastWrite][CPU]);
  466.   printf("Sequential output time: %.1f / %.1f / %.1f%%\n\n",
  467.     delta[(int) FastWrite][Elapsed] + delta[(int) Putc][Elapsed],
  468.     delta[(int) FastWrite][CPU] + delta[(int) Putc][CPU],
  469.     (delta[(int) FastWrite][CPU] + delta[(int) Putc][CPU])
  470.     / (delta[(int) FastWrite][Elapsed] + delta[(int) Putc][Elapsed])
  471.     * 100.0);
  472.  
  473.   printf("\nSequential input\n");
  474.   printf("getc() bytes/sec: %d / %d / %.1f%%\n",
  475.     (int) (((double) size) / delta[(int) Getc][Elapsed]),
  476.     (int) (((double) size) / delta[(int) Getc][CPU]),
  477.     delta[(int) Getc][CPU] / delta[(int) Getc][Elapsed] * 100.0);
  478.   printf("read() bytes/sec: %d / %d / %.1f%%\n",
  479.     (int) (((double) size) / delta[(int) FastRead][Elapsed]),
  480.     (int) (((double) size) / delta[(int) FastRead][CPU]),
  481.     delta[(int) FastRead][CPU] / delta[(int) FastRead][Elapsed] * 100.0);
  482.   printf("getc() multiplier: %.1f / %.1f\n",
  483.     delta[(int) Getc][Elapsed] / delta[(int) FastRead][Elapsed],
  484.     delta[(int) Getc][CPU] / delta[(int) FastRead][CPU]);
  485.   printf("Sequential input time: %.1f / %.1f / %.1f%%\n\n",
  486.     delta[(int) Getc][Elapsed] + delta[(int) FastRead][Elapsed],
  487.     delta[(int) Getc][CPU] + delta[(int) FastRead][CPU],
  488.     (delta[(int) Getc][CPU] + delta[(int) FastRead][CPU])
  489.     / (delta[(int) Getc][Elapsed] + delta[(int) FastRead][Elapsed])
  490.     * 100.0);
  491.  
  492.   printf("\nSequential rewrite\n");
  493.   printf("Sequential rewrite bytes/sec: %d / %d / %.1f%%\n",
  494.     (int) (((double) size) / delta[(int) ReWrite][Elapsed]),
  495.     (int) (((double) size) / delta[(int) ReWrite][CPU]),
  496.     delta[(int) ReWrite][CPU] / delta[(int) ReWrite][Elapsed] * 100.0);
  497.   printf("Sequential rewrite time: %.1f / %.1f / %.1f%%\n",
  498.     delta[(int) ReWrite][Elapsed], delta[(int) ReWrite][CPU],
  499.     delta[(int) ReWrite][CPU] / delta[(int) ReWrite][Elapsed] * 100.0);
  500.  
  501.   printf("\nRandom I/O\n");
  502.   printf("Random reads/sec: %.1f / %.1f / %.1f%%\n",
  503.     ((double) lseek_count) / delta[(int) Binary][Elapsed],
  504.     ((double) lseek_count) / delta[(int) Binary][CPU],
  505.     delta[(int) Binary][CPU] / delta[(int) Binary][Elapsed] * 100.0);
  506. }
  507.  
  508. static void
  509. newfile(name, fd, stream, create)
  510.   char *   name;
  511.   int *    fd;
  512.   FILE * * stream;
  513.   int      create;
  514. {
  515.   if (create)
  516.   { /* create from scratch */
  517.     if (unlink(name) == -1 && *fd != -1)
  518.       io_error("unlink");
  519.     *fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0777);
  520.   } /* create from scratch */
  521.   else
  522.     *fd = open(name, O_RDWR, 0777);
  523.  
  524.   if (*fd == -1)
  525.     io_error(name);
  526.   *stream = fdopen(*fd, "r+");
  527.   if (*stream == NULL)
  528.     io_error("fdopen");
  529. }
  530.  
  531. static void
  532. usage()
  533. {
  534.   fprintf(stderr, "usage: %s [-d scratch-directory] [-s size-in-megabytes]\n",
  535.     myname);
  536.   exit(1);
  537. }
  538.  
  539. static void
  540. timestamp()
  541. {
  542.   last_timestamp = time_so_far();
  543.   last_cpustamp = cpu_so_far();
  544. }
  545.  
  546. static void 
  547. get_delta_t(which)
  548.   int which;
  549. {
  550.   delta[which][Elapsed] = time_so_far() - last_timestamp;
  551.   delta[which][CPU] = cpu_so_far() - last_cpustamp;
  552. }
  553.  
  554. static double 
  555. cpu_so_far()
  556. {
  557. #ifdef i386
  558.   struct tms tms;
  559.  
  560.   if (times(&tms) == -1)
  561.     io_error("times");
  562.   return ((double) tms.tms_utime) / ((double) CLK_TCK) +
  563.     ((double) tms.tms_stime) / ((double) CLK_TCK);
  564.  
  565. #else
  566.   struct rusage rusage;
  567.  
  568.   getrusage(RUSAGE_SELF, &rusage);
  569.   return
  570.     ((double) rusage.ru_utime.tv_sec) +
  571.       (((double) rusage.ru_utime.tv_usec) / 1000000.0) +
  572.         ((double) rusage.ru_stime.tv_sec) +
  573.           (((double) rusage.ru_stime.tv_usec) / 1000000.0);
  574. #endif
  575. }
  576.  
  577. static double
  578. time_so_far()
  579. {
  580. #ifdef i386
  581.   int        val;
  582.   struct tms tms;
  583.  
  584.   if ((val = times(&tms)) == -1)
  585.     io_error("times");
  586.  
  587.   return ((double) val) / ((double) CLK_TCK);
  588.  
  589. #else
  590.   struct timeval tp;
  591.  
  592.   if (gettimeofday(&tp, (struct timezone *) NULL) == -1)
  593.     io_error("gettimeofday");
  594.   return ((double) (tp.tv_sec - basetime)) +
  595.     (((double) tp.tv_usec) / 1000000.0);
  596. #endif
  597. }
  598.  
  599. static void
  600. io_error(message)
  601.   char * message;
  602. {
  603.   char buf[BUFSIZ];
  604.  
  605.   sprintf(buf, "%s: drastic I/O error (%s)", myname, message);
  606.   perror(buf);
  607.   exit(1);
  608. }
  609.  
  610. /*
  611.  * Do a typical-of-something random I/O.  Any serious application that
  612.  *  has a random I/O bottleneck is going to be smart enough to operate
  613.  *  in a page mode, and not stupidly pull individual words out at
  614.  *  odd offsets.  To keep the cache from getting too clever, some
  615.  *  pages must be updated.  However an application that updated each of
  616.  *  many random pages that it looked at is hard to imagine.  
  617.  * However, it would be wrong to put the update percentage in as a
  618.  *  parameter - the effect is too nonlinear.  Need a profile
  619.  *  of what Oracle or Ingres or some such actually does.
  620.  * Be warned - there is a *sharp* elbow in this curve - on a 1-Mb file,
  621.  *  most substantial unix systems show >2000 random I/Os per second -
  622.  *  obviously they've cached the whole thing and are just doing buffer
  623.  *  copies.  
  624.  */
  625. static void 
  626. doseek(where, fd, update)
  627.   long where;
  628.   int  fd;
  629.   int  update;
  630. {
  631.   int  buf[BUFSIZ / IntSize];
  632.   long probe;
  633.   int  size;
  634.  
  635.   probe = (where / BUFSIZ) * BUFSIZ;
  636.   if (lseek(fd, probe, 0) != probe)
  637.     io_error("lseek in doseek");
  638.   if ((size = read(fd, (char *) buf, BUFSIZ)) == -1)
  639.     io_error("read in doseek");
  640.  
  641.   /* every so often, update a block */
  642.   if (update)
  643.   { /* update this block */
  644.  
  645.     /* touch a word */
  646.     buf[((int) random() % (size/IntSize - 2)) + 1]--;
  647.     if (lseek(fd, (long) probe, 0) != probe)
  648.       io_error("lseek in doseek update");
  649.     if (write(fd, (char *) buf, size) == -1)
  650.       io_error("write in doseek");
  651.   } /* update this block */
  652. }
  653.   
  654. #ifdef i386
  655. static char randseed[] = "ioioio";
  656.  
  657. static long
  658. random()
  659. {
  660.   return nrand48(randseed);
  661. }
  662. #endif
  663.