/* diskrate.c */ /* derived from Tom Watson's iorate by Patrick McGehearty & George Bier * * Copyright (C) 1991, Convex Computer Corp * uses gettimeofday instead of time for better accuracy * does multiple file writes to flush first file from buffer cache * before measuring read rate for first file. Performs * sync operations to force written files to go to disk. * Purpose of program is to measure throughput to disk * with minimum benefit of buffer cache. (see note below) * * */ /* to eliminate buffer cache measurements, it is recommended that the diskrate program perform a write leaving the written file the (-l option), then umount and mount the filesystem, then do the read */ #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) #include #include #include #include #include #include /* Milind Saraph, to keep Solaris happy */ #include char *malloc(), *ctime(); int bsize; /* the block size */ int nbytes; /* the total number of bytes for output, input */ int ebytes; /* the total number of bytes for extra files */ char *filename = NULL; char *extraname = NULL; /* the file name used for cache flushing */ int extra=0; int fd1,fd2; /* the file's file descriptor */ struct stat filestat; /* the file's stat info */ struct timeval before,after,aftersync,deltat,sumtime,avetime; long timecnt; long starttime, stoptime; /* the starting and ending times */ char *buf = NULL; /* the i/o buffer */ long clockoverhead; long syncoverhead; long deltausec; int readflag = 0; int writeflag = 0; int leave = 0; /*VARARGS1*/ p(a,b,c,d,e,f) char *a; { fprintf(stderr,a,b,c,d,e,f); } /*VARARGS1*/ fatal(a,b,c,d,e,f) char *a; { fprintf(stderr,a,b,c,d,e,f); exit(3); } deltatime(last,first) struct timeval *last,*first; { deltat.tv_usec = last->tv_usec - first->tv_usec; deltat.tv_sec = last->tv_sec - first->tv_sec; if (deltat.tv_usec < 0) { deltat.tv_usec += 1000000; deltat.tv_sec -= 1; } } addtime() { timecnt++; sumtime.tv_usec += deltat.tv_usec; sumtime.tv_sec += deltat.tv_sec; if (sumtime.tv_usec >= 1000000) { sumtime.tv_usec -= 1000000; sumtime.tv_sec += 1; } } paverate() { double useconds; useconds = (double) sumtime.tv_sec * 1000000.0 + (double)sumtime.tv_usec; p("Ave Write: %.3f mbytes/sec.\n",((double) nbytes * timecnt) / useconds); sumtime.tv_usec = 0; sumtime.tv_sec = 0; timecnt = 0; } main(argc,argv) /* diskrate - Measure the i/o rate of a system */ char **argv; { int filecnt; /* count of number -f arguments */ extern int optind; extern char *optarg; int c; sumtime.tv_usec = 0; sumtime.tv_sec = 0; timecnt = 0; filecnt = 0; extra = 0; readflag = 0; writeflag = 0; /* set up some reasonable defaults */ bsize = 64 * 1024; nbytes = 32 * 1024 * 1024; ebytes = 32 * 1024 * 1024; extraname = "/tmp/diskratex"; while ((c = getopt(argc, argv, "b:e:f:n:s:wrl")) != EOF) switch (c) { case 'b': bsize = atoi(optarg); while ( isdigit(*optarg)) ++optarg; if ((*optarg == 'k') || (*optarg == 'K')) bsize *= 1024; if ((*optarg == 'm') || (*optarg == 'M')) bsize *= 1024*1024; break; case 'n': nbytes = atoi(optarg); while ( isdigit(*optarg)) ++optarg; if ((*optarg == 'k') || (*optarg == 'K')) nbytes *= 1024; if ((*optarg == 'm') || (*optarg == 'M'))nbytes *= 1024 * 1024; break; case 'f': filename = optarg; break; case 'e': extra = 1; /* the extra file has been made obsolete, use mount/umount instead */ extraname = optarg; break; case 'r': readflag = 1; /* only do read */ break; case 'R': readflag = 1; /* only do read */ break; case 'w': writeflag = 1; /* only do writes */ break; case 'W': writeflag = 1; /* only do writes */ break; case 's': ebytes = atoi(optarg); while ( isdigit(*optarg)) ++optarg; if ((*optarg == 'k') || (*optarg == 'K')) ebytes *= 1024; if ((*optarg == 'm') || (*optarg == 'M'))ebytes *= 1024 * 1024; break; case 'l': leave = 1; break; case 'L': leave = 1; break; default: printusage(); } if (filename == NULL) { fprintf(stderr,"-f filename missing\n\n"); printusage(); } findclocktime(); findsynctime(); setup(); runit(); } printusage() { fprintf(stderr, "Usage: diskrate [-n filesize] [-b writeblocksize] -f filename\n"); fprintf(stderr, " [-e extrafile] [-s extrafilesize] [-r -w -l]\n"); fprintf(stderr,"\t\t-r only do a read\n"); fprintf(stderr,"\t\t-w only do writes\n"); fprintf(stderr,"\t\t-l leave file after write\n"); exit(2); } findclocktime() { register int i; long sumt,cnt; sumt = 0; cnt = 0; measureclock(); /* cache clock code */ for ( i = 0; i < 100; i++) { measureclock(); if (deltausec < 100 ) /* omit heavy overhead times */ { sumt += deltausec; cnt++;} } if (cnt == 0) clockoverhead = 20; /* special case, should never occur */ else clockoverhead = sumt/cnt; } measureclock() { (void) gettimeofday(&before, (struct timezone *) 0); (void) gettimeofday(&after, (struct timezone *) 0); after.tv_usec -= before.tv_usec; after.tv_sec -= before.tv_sec; deltausec = after.tv_sec * 1000000 + after.tv_usec; } findsynctime() { register int i; long sumt,cnt; sumt = 0; cnt = 0; measuresync(); /* cache sync code */ for ( i = 0; i < 20; i++) { measuresync(); if (deltausec < 10000 ) /* omit heavy overhead times */ { sumt += deltausec; cnt++;} } if (cnt == 0) syncoverhead = 300; /* special case, should never occur */ else syncoverhead = sumt/cnt; } measuresync() { (void) gettimeofday(&before, (struct timezone *) 0); sync(); sync(); (void) gettimeofday(&after, (struct timezone *) 0); after.tv_usec -= before.tv_usec; after.tv_sec -= before.tv_sec; deltausec = after.tv_sec * 1000000 + after.tv_usec; } setup() { register long i; fd1 = open(filename, O_CREAT|O_RDWR, 0666); if (fd1 < 0) fatal("Can't open/create %s.\n", filename); if (fstat(fd1, &filestat) < 0) fatal("Can't stat %s.\n", filename); if (bsize == 0) bsize = filestat.st_blksize; /* file sys bsize */ nbytes = roundup (nbytes,bsize); /* no odd endings */ if(extra != 0) { fd2 = open(extraname, O_CREAT|O_RDWR, 0666); if (fd2 < 0) fatal("Can't open/create %s.\n", extraname); if (fstat(fd2, &filestat) < 0) fatal("Can't stat %s.\n", extraname); } /* get an i/o buffer */ if (buf == NULL) buf = malloc((unsigned)bsize); if (buf == NULL) fatal("Can't allocate %d byte buffer.\n", bsize); for (i=0; i < bsize; i++) buf[i] = i; } runit() { register i; if (readflag == 0) { /* write measurement */ dowrite(fd1); } if(extra != 0) quickwrite(fd2); /* read measurement */ if (writeflag == 0) doread(fd1); (void)close(fd1); if (leave == 0 && readflag == 0 ) { (void)unlink(filename); } if(extra != 0) { (void)close(fd2); (void)unlink(extraname); } return 0; } dowrite(fd) int fd; { register int i; double usecs; sync(); /* insure disk cache is empty at start of measurement */ sleep(2); /* give disk buffer time to clear */ sync(); /* complete clearing of disk cache */ (void)gettimeofday(&before, (struct timezone *) 0); /* get start */ for (i=nbytes; i > 0; i -= bsize) if (write(fd,buf,bsize) != bsize) { perror("write"); exit(9); } (void)gettimeofday(&after, (struct timezone *) 0); /* force file write */ fsync(fd); (void)gettimeofday(&aftersync, (struct timezone *) 0); deltatime(&after,&before); /* compute rate before cache flush */ usecs = (double)deltat.tv_sec * 1000000.0 + (double)(deltat.tv_usec - clockoverhead); p("Chars written: %-10d\n",nbytes); if (usecs <= 0.0) p("Buffered Write: 0.0 seconds "); else p("Buffered Write: %6.3f mbytes/sec. ", ((double) nbytes / usecs) ); deltatime(&aftersync,&before); /* compute rate after cache flush */ usecs = (double)deltat.tv_sec * 1000000.0 + (double)(deltat.tv_usec - clockoverhead); if (usecs <= 0.0) p("Flushed Write: 0.0 seconds\n"); else { addtime(); p("Flushed Write: %6.3f mbytes/sec.\n", ((double) nbytes / usecs ) ); } } quickwrite(fd) int fd; { register int i; double usecs; for (i=ebytes; i > 0; i -= bsize) if (write(fd,buf,bsize) != bsize) { perror("write"); exit(9); } /* force file write */ fsync(fd); } doread(fd) int fd; { register int i; double usecs; sync(); /* insure disk cache is empty at start of measurement */ sleep(2); /* give disk buffer time to clear */ (void)lseek(fd,0,0); /* rewind */ (void)gettimeofday(&before, (struct timezone *) 0); for (i=nbytes; i > 0; i -= bsize) if (read(fd,buf,bsize) != bsize) { perror("read"); exit(9); } (void)gettimeofday(&after, (struct timezone *) 0); deltatime(&after,&before); usecs = (double)deltat.tv_sec * 1000000.0 + (double)(deltat.tv_usec - clockoverhead); p("Chars Read: %-10d\n",nbytes); if (usecs <= 0.0) p("Sorry, read took 0 time- time has stopped.\n"); else p("Read Rate: %6.3f mbytes/sec.\n", ((double) nbytes / usecs) ); }