home *** CD-ROM | disk | FTP | other *** search
- From: tbray@watsol.waterloo.edu (Tim Bray)
- Newsgroups: alt.sources
- Subject: [comp.arch] Unix Filesystem Benchmark Results (long)
- Message-ID: <12022@stag.math.lsa.umich.edu>
- Date: 14 May 90 02:08:05 GMT
-
- Archive-name: fsx/13-May-90
- Original-posting-by: tbray@watsol.waterloo.edu (Tim Bray)
- Original-subject: Unix Filesystem Benchmark Results (long)
- Reposted-by: emv@math.lsa.umich.edu (Edward Vielmetti)
-
- [This is an experimental alt.sources re-posting from the newsgroup(s)
- comp.arch. Comments on this service to emv@math.lsa.umich.edu
- (Edward Vielmetti).]
-
-
- Howdy folks. We've been hacking text databases here on the New OED project
- for years, and have been disappointed at the performance of Unix filesystems
- in general, and also at the tools available for measuring that performance.
-
- So I wrote a program to address this need. It fails to meet the SPEC
- criterion of being real working code that does something useful, but it does
- produce very interesting numbers, most of which I believe.
-
- This posting includes a summary of the results of running the benchmark on all
- the machines I could get my hands on, some subjective commentary, and the
- source code for the benchmark, the comments in which should be read by
- everyone before beginning to think about believing these numbers.
-
- Cheers, Tim Bray, Open Text Systems, Waterloo, Ont.
-
- -------Sequential Output-------- ---Sequential Input-- --Random-
- -Per Char- --Block--- -Rewrite-- -Per Char- --Block--- --Seeks--
- K/sec %CPU K/sec %CPU K/sec %CPU K/sec %CPU K/sec %CPU /sec %CPU
-
- DEC 5810 330 73.6 690 62.8 195 29.0 274 61.6 649 45.9 27.7 4.9
- 434 92.7 732 63.3 206 29.2 382 80.7 701 47.6 34.3 5.1
- 418 88.1 718 65.0 196 28.7 386 81.7 744 52.3 27.1 5.8
-
- MipsM/2K 316 35.2 381 8.8 302 11.4 611 67.9 1369 27.4 28.5 1.2
- 345 38.1 435 10.0 226 8.8 399 44.1 876 17.3 21.3 1.3
- 362 40.0 440 10.5 371 15.0 785 89.1 1762 37.3 26.4 1.8
-
- NeXT 243 92.0 352 48.8 210 38.9 217 95.0 782 72.4 28.4 12.7
- 243 92.9 360 50.0 216 40.7 251 95.8 773 71.4 28.7 14.3
- 245 93.1 346 48.2 215 40.3 218 95.7 777 71.3 26.7 13.7
-
- Seq.Symm. 178 99.1 951 90.7 307 48.1 151 95.7 694 57.2 35.6 6.6
- 176 98.2 926 85.8 285 43.4 150 95.4 655 51.5 33.4 6.7
-
- VAX 8650 197 81.7 262 22.3 116 14.1 255 81.5 434 16.6 22.1 2.5
- 186 77.7 214 18.6 99 12.7 144 46.9 361 15.1 16.8 2.2
-
- Sun 4/260 298 83.2 791 58.2 296 26.3 312 94.5 840 44.8 ****
- 328 92.6 796 59.7 288 26.4 298 91.2 726 39.7 ****
-
- 386 229 91.9 241 27.5 207 46.4 209 91.9 437 56.0 34.4 7.0
-
- IBM R 6000 654 89.0 1178 22.6 434 13.1 750 94.2 1539 20.9 39.6 3.0
- 712 96.5 1258 24.1 444 13.4 727 91.3 1546 21.7 33.7 2.3
-
- Commentary: feel free to disbelieve
-
- DEC 5810: Ultrix 3.1C-0 (Rev 42), 1.2G filesystem on disk, 91% full.
- The R2000 in here is slower than the R3000 in the Mips M/2K, but the
- I/O rates are competitive; however this box has to burn more CPU to
- achieve those rates. Note that block input and output are about the
- same speed, making it slower than the MIPS on input but faster on
- output.
-
- Mips M/2000: UMIPS 4.0, 564M filesystem on MIPS disk, 83% full.
- This is a very fast CPU, and it shows in the %CPU columns. The
- block-mode input number is very good - over 1.25 effective
- megabytes/sec off the disk. Note the block-in vs. block-out anomaly
- with respect to the DEC machine. Also, the seeks/sec. are unexciting.
-
- NeXT: SW Version 1.0, 446M filesystem on NeXT HD, 82% full.
- One of the slower CPUs in the crowd, and it shows. Given its price
- relative to some of these other boxes, this is excellent performance;
- the NeXT is supposed to have fancy DMAs. Didn't have an optical I
- could write on, but that would be very interesting.
-
- Sequent: 10 386 processors, Dynix 3.0.17.9, 564M filesystem, 82% full.
- The individual processors in here are fairly-slow 386es, and that shows
- in the %CPU column. But burning 100% of a 386 to do the filesystem and
- leaving the other 9 to do useful work seems smart to me. The block I/O
- throughput and in particular the seeks/second are very good. Looks
- like a good database machine.
-
- VAX 8650: 4.3bsd, 584M filesystem on CDC Wren VI on UDA, 33% full.
- Well, the myth of VAX as good I/O machine seems to be biting the dust
- here. An 8650 is a pretty fast CPU, and has an elaborate I/O
- architecture. There must be a bottleneck here - candidates are the
- UDA, the Unibus, and 4.3bsd. Note the %CPU figures are very
- low, which may just be a result of the I/O being so slow, or some CISC
- artifact. It's even slow at seeking. Seven years ago, we used to have
- some 780s with RP07's on MASSBUSes - I wouldn't be surprised if they
- measured faster than this.
-
- Sun 4/260: SunOS 4.0.3, 526M filesystem on Fuji M2344, 87% full.
- Not bad, consistently in the top half. Some of the speed seems to be
- purchased by burning CPU - the %CPU figures are quite high. Couldn't
- get meaningful numbers on seeks/sec because we don't have the disk
- space to spare. With 32M of memory, operating on a 50M file, the
- caching drove the seeks/sec up over 60.
-
- 386: 20 MHz AMI motherboard, 386/ix V2.0.2, 180M filesys on MaxStor ESDI
- disk, 51% full.
- I think this is pretty damn good performance for a machine which is
- an order of magnitude less in cost than some of the other boxes here.
- In particular, its random I/O performance seems to rival that of the
- big RISC chips, both the DEC and MIPS implementations (8M memory for a
- 50M file, so it ain't caching much). The scuttlebutt says that these
- can be made still faster using caching controllers.
-
- IBM R6000, Beta Test AIX, 450M filesystem on "Cheap SCSI", 34% full.
- Was never able to run the tests on an idle system, something was
- burning CPU all the time, if vmstat(1) is to be believed, but I
- couldn't find a candidate using ps(1) - except perhaps something called
- "kproc", but that sounds like a fake kernel task. Also, there was only
- about 50 Mb free on the filesystem, so the sysadmin did something with
- "logical volumes" and the "chfs(1)" command then the filesys was 250M
- bigger - might this be muddying the waters? A test run before the chfs
- showed *unbelievable* rates, like 5+ Mb system coming off the filesys
- using read(2). Anyhow, the conclusion is clear - this puppy is an
- I/O monster.
-
- --cut here---------------------------------------------------------------
-
- /*
- *
- * fsx is a File System eXerciser. It is a benchmark that attempts to
- * quantify the performance of several filesystem operations that have been
- * observed to be bottlenecks in I/O-intensive applications, specifically
- * the text database work done in connection with the New Oxford English
- * Dictionary Project at the University of Waterloo.
- *
- * It performs a series of tests on a file of known size. By default, that
- * size is 50 Mb. For each test, fsx reports the bytes processed per
- * elapsed second, per CPU second, and the % CPU usage (user and system).
- *
- * In each case, some care is taken to keep optimizers, no matter how
- * clever, from noticing it's all bogus. The idea is to make sure that
- * these are real transfers from user space to the physical disk. The
- * tests are:
- *
- * 1. Sequential Output
- *
- * 1.1 Per-Character. The file is written using the putc() stdio macro.
- * The loop that does the writing should be small enough to fit into any
- * reasonable I-cache. The CPU exercised here is that required to do the
- * stdio code plus the OS file space allocation.
- *
- * 1.2 Block. The file is created using write(2). The CPU consumption
- * should be just the OS file space allocation.
- *
- * 1.3 Rewrite. Each BUFSIZ of the file is read with read(2), dirtied, and
- * rewritten with write(2), requiring an lseek(2). Since no space
- * allocation is done, and the I/O is well-localized, this should test the
- * effectiveness of the filesystem cache and the speed of data transfer.
- *
- * 2. Sequential Input
- *
- * 2.1 Per-Character. The file is read using the getc() stdio macro. Once
- * again, the inner loop is small. This should exercise only stdio and
- * sequential input.
- *
- * 2.2 Block. The file is read using read(2). This should be a very pure
- * test of sequential input performance.
- *
- * 3. Random Seeks
- *
- * This test seeks 1000 times to locations in the file specified by
- * random() in bsd systems, drand48() on sysV systems. In each case, the
- * block is read with read(2). In 10% of cases, it is dirtied and written
- * back with write(2).
- *
- * AXIOM: For any unix filesystem, the effective number of lseek(2) calls
- * per second declines asymptotically to near 30, once the effect of
- * caching is defeated.
- *
- * The size of the file has a strong nonlinear effect on the results of
- * this test. Many Unix systems that have the memory available will make
- * aggressive efforts to cache the whole thing, and report random I/O rates
- * in the thousands per second, which is ridiculous. As an extreme
- * example, an IBM RISC 6000 with 64 Mb of memory reported 3,722 per second
- * on a 50 Mb file.
- *
- * COPYRIGHT NOTICE:
- * Copyright (c) Tim Bray, 1990.
- * Everybody is hereby granted rights to use, copy, and modify this program,
- * provided only that the copyright notice above and the disclaimer below
- * are preserved without change.
- * DISCLAIMER:
- * This program is provided AS IS with no warranty of any kind, and
- * The author makes no representation with respect to the adequacy of this
- * program for any particular purpose or with respect to its adequacy to
- * produce any particular result, and
- * The author shall not be liable for loss or damage arising out of
- * the use of this program regardless of how sustained, and
- * In no event shall the author be liable for special, direct, indirect
- * or consequential damage, loss, costs or fees or expenses of any
- * nature or kind.
- */
-
- #include <stdio.h>
- #include <fcntl.h>
- #include <sys/types.h>
- #include <sys/time.h>
- #ifdef i386
- #include <limits.h>
- #include <sys/times.h>
- #else
- #include <sys/resource.h>
- #endif
-
- #define IntSize (4)
- #define Elapsed (0)
- #define CPU (1)
- #define Searches (1000)
- #define UpdateSeek (10)
-
- static double cpu_so_far();
- static void doseek();
- static void get_delta_t();
- static void io_error();
- static void newfile();
- static void report();
- static double time_so_far();
- static void timestamp();
- static void usage();
- #ifdef i386
- static long random();
- #endif
-
- typedef enum
- {
- Putc,
- ReWrite,
- FastWrite,
- Getc,
- FastRead,
- Binary,
- TestCount
- } tests_t;
-
- static int basetime;
- static double delta[(int) TestCount][2];
- static char * myname;
- static double last_cpustamp = 0.0;
- static double last_timestamp = 0.0;
- static int lseek_count = 0;
-
- main(argc, argv)
- int argc;
- char * argv[];
- {
- int buf[BUFSIZ / IntSize];
- int bufindex;
- int chars[256];
- char * dir;
- int fd;
- char name[BUFSIZ];
- int next;
- int size;
- FILE * stream;
- int words;
-
- myname = argv[0];
- fd = -1;
- basetime = (int) time((time_t *) NULL);
- size = 50;
- dir = ".";
-
- for (next = 1; next < argc - 1; next++)
- if (argv[next][0] == '-')
- { /* option? */
- if (strcmp(argv[next] + 1, "d") == 0)
- dir = argv[next + 1];
- else if (strcmp(argv[next] + 1, "s") == 0)
- size = atoi(argv[next + 1]);
- else
- usage();
- next++;
- } /* option? */
- else
- usage();
-
- if (size < 1)
- usage();
- size *= (1024 * 1024);
- sprintf(name, "%s/fsx.%d", dir, getpid());
- printf("File '%s', size: %d\n", name, size);
- fflush(stdout);
-
- /*
- * Fill up a file, writing it a char at a time with the stdio putc()
- * facility. This models the activity of a negligible-CPU filter
- * and exercises kernel file space allocation. Should be measured with
- * disks of varying degrees of fullness.
- */
- printf("Writing with putc()...");
- newfile(name, &fd, &stream, 1);
- fflush(stdout);
- timestamp();
- for (words = 0; words < size; words++)
- if (putc(words & 0x7f, stream) == EOF)
- io_error("putc");
-
- /*
- * note that we always close the file before measuring time, in an
- * effort to force as much of the I/O out as we can
- */
- if (fclose(stream) == -1)
- io_error("fclose after putc");
- get_delta_t((int) Putc);
- printf("done\n");
- fflush(stdout);
-
- /*
- * Now read & rewrite it, a block at a time. Change one word in each block
- * in case somebody's being clever about rewritten dirty blocks.
- * This exercises sequential read-write performance and hence the cache,
- * but avoids any space-allocation work.
- */
- newfile(name, &fd, &stream, 0);
- if (lseek(fd, (off_t) 0, 0) == (off_t) -1)
- io_error("lseek(2) before rewrite");
- printf("Rewriting...");
- fflush(stdout);
- timestamp();
- bufindex = 0;
- do
- { /* while we can read a block */
- if ((words = read(fd, (char *) buf, BUFSIZ)) == -1)
- io_error("read(2)");
- if (bufindex == words / IntSize)
- bufindex = 0;
- buf[bufindex++]++;
- if (lseek(fd, (off_t) -words, 1) == -1)
- io_error("relative lseek(2)");
- if (write(fd, (char *) buf, words) == -1)
- io_error("re write(2)");
- } /* while we can read a block */
- while (words == BUFSIZ);
- if (close(fd) == -1)
- io_error("close after rewrite");
- get_delta_t((int) ReWrite);
- printf("done\n");
- fflush(stdout);
-
- /*
- * Now rewrite the whole file from scratch, but a block at a time rather
- * than with stdio. Exercises space allocation a little more stringently,
- * and by comparison with the putc test, quantifies stdio overhead a bit.
- * Once again, dirty each block.
- */
- newfile(name, &fd, &stream, 1);
- printf("Writing intelligently...");
- for (words = 0; words < BUFSIZ / IntSize; words++)
- buf[words] = 0;
- fflush(stdout);
- timestamp();
- for (words = bufindex = 0; words < (size / BUFSIZ); words++)
- { /* for each word */
- if (bufindex == (BUFSIZ/IntSize))
- bufindex = 0;
- buf[bufindex++]++;
- if (write(fd, (char *) buf, BUFSIZ) == -1)
- io_error("write(2)");
- } /* for each word */
- if (close(fd) == -1)
- io_error("close after fast write");
- get_delta_t((int) FastWrite);
- printf("done\n");
- fflush(stdout);
-
- /*
- * Now read them all back with getc, excercising default character-
- * at-a-time input. Do a character frequency count just to fool
- * any optimizers that may notice that they're not being used.
- */
- newfile(name, &fd, &stream, 0);
- for (words = 0; words < 256; words++)
- chars[words] = 0;
- printf("Reading with getc()...");
- fflush(stdout);
- timestamp();
- for (words = 0; words < size; words++)
- { /* for each byte */
- if ((next = getc(stream)) == EOF)
- io_error("getc(3)");
- chars[next]++;
- } /* for each byte */
- if (fclose(stream) == -1)
- io_error("fclose after getc");
- get_delta_t((int) Getc);
- printf("done\n");
- fflush(stdout);
-
- /* use the frequency count */
- for (words = 0; words < 256; words++)
- sprintf((char *) buf, "%d", chars[words]);
-
- /*
- * Now suck it in, BUFSIZ at a time, as fast as we can.
- */
- newfile(name, &fd, &stream, 0);
- if (lseek(fd, 0L, 0) == -1)
- io_error("lseek before read");
- printf("Reading intelligently...");
- fflush(stdout);
- timestamp();
- do
- if ((words = read(fd, (char *) buf, BUFSIZ)) == -1)
- io_error("read(2)");
- while (words);
- if (close(fd) == -1)
- io_error("close after read");
- get_delta_t((int) FastRead);
- printf("done\n");
- fflush(stdout);
-
- /*
- * Now do some random I/O. Originally these were binary searches, but
- * that is well-behaved, since the first few seeks are always the same.
- * Probably an application that was doing this kind of thing would keep
- * its own cache of the top few levels of the tree or whatever - we're
- * just interested in 'How much random I/O can be done?'
- */
- newfile(name, &fd, &stream, 0);
- timestamp();
- printf("Seeking...");
- fflush(stdout);
- for (lseek_count = 0; lseek_count < Searches; lseek_count++)
- doseek(random() % size, fd, ((lseek_count % UpdateSeek) == 0));
- if (close(fd) == -1)
- io_error("close after read");
- get_delta_t((int) Binary);
- printf("done\n");
- fflush(stdout);
-
- report(size);
- unlink(name);
- }
-
- static void
- report(size)
- int size;
- {
- printf("\n");
- printf("Times reported are elapsed / cpu / %%cpu usage.\n");
-
- printf("\nSequential output\n");
- printf("putc() bytes/sec: %d / %d / %.1f%%\n",
- (int) (((double) size) / delta[(int) Putc][Elapsed]),
- (int) (((double) size) / delta[(int) Putc][CPU]),
- delta[(int) Putc][CPU] / delta[(int) Putc][Elapsed] * 100.0);
- printf("write() bytes/sec: %d / %d / %.1f%%\n",
- (int) (((double) size) / delta[(int) FastWrite][Elapsed]),
- (int) (((double) size) / delta[(int) FastWrite][CPU]),
- delta[(int) FastWrite][CPU] / delta[(int) FastWrite][Elapsed] * 100.0);
- printf("putc() multiplier: %.1f / %.1f\n",
- delta[(int) Putc][Elapsed] / delta[(int) FastWrite][Elapsed],
- delta[(int) Putc][CPU] / delta[(int) FastWrite][CPU]);
- printf("Sequential output time: %.1f / %.1f / %.1f%%\n\n",
- delta[(int) FastWrite][Elapsed] + delta[(int) Putc][Elapsed],
- delta[(int) FastWrite][CPU] + delta[(int) Putc][CPU],
- (delta[(int) FastWrite][CPU] + delta[(int) Putc][CPU])
- / (delta[(int) FastWrite][Elapsed] + delta[(int) Putc][Elapsed])
- * 100.0);
-
- printf("\nSequential input\n");
- printf("getc() bytes/sec: %d / %d / %.1f%%\n",
- (int) (((double) size) / delta[(int) Getc][Elapsed]),
- (int) (((double) size) / delta[(int) Getc][CPU]),
- delta[(int) Getc][CPU] / delta[(int) Getc][Elapsed] * 100.0);
- printf("read() bytes/sec: %d / %d / %.1f%%\n",
- (int) (((double) size) / delta[(int) FastRead][Elapsed]),
- (int) (((double) size) / delta[(int) FastRead][CPU]),
- delta[(int) FastRead][CPU] / delta[(int) FastRead][Elapsed] * 100.0);
- printf("getc() multiplier: %.1f / %.1f\n",
- delta[(int) Getc][Elapsed] / delta[(int) FastRead][Elapsed],
- delta[(int) Getc][CPU] / delta[(int) FastRead][CPU]);
- printf("Sequential input time: %.1f / %.1f / %.1f%%\n\n",
- delta[(int) Getc][Elapsed] + delta[(int) FastRead][Elapsed],
- delta[(int) Getc][CPU] + delta[(int) FastRead][CPU],
- (delta[(int) Getc][CPU] + delta[(int) FastRead][CPU])
- / (delta[(int) Getc][Elapsed] + delta[(int) FastRead][Elapsed])
- * 100.0);
-
- printf("\nSequential rewrite\n");
- printf("Sequential rewrite bytes/sec: %d / %d / %.1f%%\n",
- (int) (((double) size) / delta[(int) ReWrite][Elapsed]),
- (int) (((double) size) / delta[(int) ReWrite][CPU]),
- delta[(int) ReWrite][CPU] / delta[(int) ReWrite][Elapsed] * 100.0);
- printf("Sequential rewrite time: %.1f / %.1f / %.1f%%\n",
- delta[(int) ReWrite][Elapsed], delta[(int) ReWrite][CPU],
- delta[(int) ReWrite][CPU] / delta[(int) ReWrite][Elapsed] * 100.0);
-
- printf("\nRandom I/O\n");
- printf("Random reads/sec: %.1f / %.1f / %.1f%%\n",
- ((double) lseek_count) / delta[(int) Binary][Elapsed],
- ((double) lseek_count) / delta[(int) Binary][CPU],
- delta[(int) Binary][CPU] / delta[(int) Binary][Elapsed] * 100.0);
- }
-
- static void
- newfile(name, fd, stream, create)
- char * name;
- int * fd;
- FILE * * stream;
- int create;
- {
- if (create)
- { /* create from scratch */
- if (unlink(name) == -1 && *fd != -1)
- io_error("unlink");
- *fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0777);
- } /* create from scratch */
- else
- *fd = open(name, O_RDWR, 0777);
-
- if (*fd == -1)
- io_error(name);
- *stream = fdopen(*fd, "r+");
- if (*stream == NULL)
- io_error("fdopen");
- }
-
- static void
- usage()
- {
- fprintf(stderr, "usage: %s [-d scratch-directory] [-s size-in-megabytes]\n",
- myname);
- exit(1);
- }
-
- static void
- timestamp()
- {
- last_timestamp = time_so_far();
- last_cpustamp = cpu_so_far();
- }
-
- static void
- get_delta_t(which)
- int which;
- {
- delta[which][Elapsed] = time_so_far() - last_timestamp;
- delta[which][CPU] = cpu_so_far() - last_cpustamp;
- }
-
- static double
- cpu_so_far()
- {
- #ifdef i386
- struct tms tms;
-
- if (times(&tms) == -1)
- io_error("times");
- return ((double) tms.tms_utime) / ((double) CLK_TCK) +
- ((double) tms.tms_stime) / ((double) CLK_TCK);
-
- #else
- struct rusage rusage;
-
- getrusage(RUSAGE_SELF, &rusage);
- return
- ((double) rusage.ru_utime.tv_sec) +
- (((double) rusage.ru_utime.tv_usec) / 1000000.0) +
- ((double) rusage.ru_stime.tv_sec) +
- (((double) rusage.ru_stime.tv_usec) / 1000000.0);
- #endif
- }
-
- static double
- time_so_far()
- {
- #ifdef i386
- int val;
- struct tms tms;
-
- if ((val = times(&tms)) == -1)
- io_error("times");
-
- return ((double) val) / ((double) CLK_TCK);
-
- #else
- struct timeval tp;
-
- if (gettimeofday(&tp, (struct timezone *) NULL) == -1)
- io_error("gettimeofday");
- return ((double) (tp.tv_sec - basetime)) +
- (((double) tp.tv_usec) / 1000000.0);
- #endif
- }
-
- static void
- io_error(message)
- char * message;
- {
- char buf[BUFSIZ];
-
- sprintf(buf, "%s: drastic I/O error (%s)", myname, message);
- perror(buf);
- exit(1);
- }
-
- /*
- * Do a typical-of-something random I/O. Any serious application that
- * has a random I/O bottleneck is going to be smart enough to operate
- * in a page mode, and not stupidly pull individual words out at
- * odd offsets. To keep the cache from getting too clever, some
- * pages must be updated. However an application that updated each of
- * many random pages that it looked at is hard to imagine.
- * However, it would be wrong to put the update percentage in as a
- * parameter - the effect is too nonlinear. Need a profile
- * of what Oracle or Ingres or some such actually does.
- * Be warned - there is a *sharp* elbow in this curve - on a 1-Mb file,
- * most substantial unix systems show >2000 random I/Os per second -
- * obviously they've cached the whole thing and are just doing buffer
- * copies.
- */
- static void
- doseek(where, fd, update)
- long where;
- int fd;
- int update;
- {
- int buf[BUFSIZ / IntSize];
- long probe;
- int size;
-
- probe = (where / BUFSIZ) * BUFSIZ;
- if (lseek(fd, probe, 0) != probe)
- io_error("lseek in doseek");
- if ((size = read(fd, (char *) buf, BUFSIZ)) == -1)
- io_error("read in doseek");
-
- /* every so often, update a block */
- if (update)
- { /* update this block */
-
- /* touch a word */
- buf[((int) random() % (size/IntSize - 2)) + 1]--;
- if (lseek(fd, (long) probe, 0) != probe)
- io_error("lseek in doseek update");
- if (write(fd, (char *) buf, size) == -1)
- io_error("write in doseek");
- } /* update this block */
- }
-
- #ifdef i386
- static char randseed[] = "ioioio";
-
- static long
- random()
- {
- return nrand48(randseed);
- }
- #endif
-