norcott@databs.enet.dec.com (Bill Norcott) (05/18/91)
Attached is release V1.07 of my IOZONE benchmark. The new version adds 'auto mode', which performs a series of tests which measure read and write transfer rates on files from 1-16 MB in length, using record sizes from 512-8192 bytes. It tabulates the results. To invoke auto mode, type 'iozone auto'. This version also adds support for several operating systems including AIX & SUNOS. Bill Norcott -------------------------- CUT HERE ---------------------------- /* * "IO Zone" Benchmark Program * * Author: Bill Norcott (Bill.Norcott@nuo.mts.dec.com) * 4 Dunlap Drive * Nashua, NH 03060 * * "Copyright 1991, William D. Norcott * License to freely use and distribute this software is hereby granted * by the author, subject to the condition that this copyright notice * remains intact. The author retains the exclusive right to publish * derivative works based on this work, including, but not limited, * to revised versions of this work" * * "This test writes a 4 MEGABYTE sequential file in 512 byte chunks, then * rewinds it and reads it back. [The size of the file should be * big enough to factor out the effect of any disk cache.] * * The file is written (filling any cache buffers), and then read. If the * cache is >= 4 MB, then most if not all the reads will be satisfied from * the cache. However, if it is less than or equal to 2 MB, then NONE of * the reads will be satisfied from the cache. This is becase after the * file is written, a 2 MB cache will contain the upper 2 MB of the test * file, but we will start reading from the beginning of the file (data * which is no longer in the cache) * * In order for this to be a fair test, the length of the test file must * be AT LEAST 2X the amount of disk cache memory for your system. If * not, you are really testing the speed at which your CPU can read blocks * out of the cache (not a fair test) * * IOZONE does NOT test the raw I/O speed of your disk or system. It * tests the speed of sequential I/O to actual files. Therefore, this * measurement factors in the efficiency of you machines file system, * operating system, C compiler, and C runtime library. It produces a * measurement which is the number of bytes per second that your system * can read or write to a file. * * For V1.06, IOZONE adds the "auto test" feature. This is activated * by the command: 'iozone auto' . The auto test runs IOZONE repeatedly * using record sizes from 512 to 8192 bytes, and file sizes from 1 to 16 * megabytes. It creates a table of results. * * This program has been ported and tested on the following computer * operating systems: * * AIX Version 3 release 1 (compile: cc -Dunix -o iozone iozone.c) * MS-DOS 3.3 (Turbo C, Turbo C++, Microsoft C) * OSF/1 * SCO Unix System V release 3.2 version 2 * SUNOS 4.0.3 (compile: cc -Dnolimits -o iozone iozone.c) * Ultrix V4.1 * VAX/VMS V5.4 * XENIX 3.2 * * Acknowledgements to the following persons for their feedback on IOzone: * * Andy Puchrik, Michael D. Lawler, Krishna E. Bera, Sam Drake, John H. Hartman, * Ted Lyszczarz, Bill Metzenthen * * --- MODIFICATION HISTORY: * * 3/7/91 William D. Norcott (Bill.Norcott@nuo.mts.dec.com) * created * * 3/22/91 Bill Norcott tested on OSF/1 ... it works * * 3/24/91 Bill Norcott V1.02 -- use calloc in TURBOC to * fix bug with their malloc * * 3/25/91 Bill Norcott V1.03 -- add ifdef for XENIX * * 3/27/91 Bill Norcott V1.04 -- Includes for SCO UNIX * * 4/26/91 Bill Norcott V1.05 -- support AIX and SUNos, check * length of read() and write() * 4/26/91 Bill Norcott V1.06 -- tabulate results of a series * of tests * 5/17/91 Bill Norcott V1.07 -- use time() for VMS */ /****************************************************************** INCLUDE FILES (system-dependent) ******************************************************************/ #ifdef __MSDOS__ /* Turbo C define this way for PCs... */ #define MSDOS /* Microsoft C defines this */ #endif /* VMS and MS-DOS both have ANSI C compilers and use rand()/srand() */ #ifdef VMS_POSIX #undef VMS #define ANSI_RANDOM 1 #endif #ifdef MSDOS #define ANSI_RANDOM 1 #endif /* incl definitions of O_* flags for XENIX */ #ifdef M_XENIX #include <fcntl.h> #endif /* SCO Unix System V */ #ifdef M_UNIX #include <sys/types.h> #include <sys/fcntl.h> #endif #if defined(VMS) #define ANSI_RANDOM 1 #include <math.h> #include <unixio.h> #include <ssdef.h> #include <file.h> #include <time.h> #elif defined(MSDOS) #include <fcntl.h> #include <time.h> #elif defined(unix) #include <sys/file.h> #ifndef nolimits #include <limits.h> #endif #endif /* for systems with System V-style time, define SysVtime */ #ifdef M_SYSV #define SysVtime #endif #ifdef SysVtime #include <sys/times.h> #include <sys/param.h> #ifndef CLK_TCK #define CLK_TCK HZ #endif #endif /* for systems with BSD style time, define BSDtime */ #ifdef bsd4_2 #define BSDtime #endif #ifdef BSDtime #include <sys/time.h> #endif /****************************************************************** DEFINED CONSTANTS ******************************************************************/ #define MEGABYTES 1 /* number of megabytes in file */ #define RECLEN 512 /* number of bytes in a record */ #define FILESIZE (MEGABYTES*1024*1024) /*size of file in bytes*/ #define NUMRECS FILESIZE/RECLEN /* number of records */ #define MAXBUFFERSIZE 16*1024 /*maximum buffer size*/ #define MINBUFFERSIZE 128 #define TOOFAST 10 #define USAGE "\tUsage:\tiozone [megabytes] [record_length_in_bytes] \ [[path]filename]\n\n" #define THISVERSION "V1.06" /* Define only one of the following two.*/ /*#define NOTIME define if no time function in library*/ #define TIME /*Use time(2)function*/ #define MAXNAMESIZE 1000 /* max # of characters in filename */ #define CONTROL_STRING1 "%\t%-8d%-8d%-20d%-20d\n" #define CONTROL_STRING2 "\t%-8s%-8s%-20s%-20s\n" /****************************************************************** FUNCTION DECLARATIONS ******************************************************************/ void auto_test(); /* perform automatic test series */ static double time_so_far(); /* time since start of program */ /****************************************************************** GLOBAL VARIABLES ******************************************************************/ int auto_mode; /****************************************************************** MAIN -- entry point ******************************************************************/ main(argc,argv) int argc; char *argv[]; { int fd; char filename [MAXNAMESIZE]; /* name of temporary file */ char *default_filename="iozone.tmp"; /*default name of temporary file*/ #ifdef MSDOS char *buffer; #else char buffer [MAXBUFFERSIZE]; /*a temporary data buffer*/ #endif int i, status; unsigned long megabytes = MEGABYTES, goodmegs; unsigned long reclen = RECLEN, goodrecl; unsigned long filesize = FILESIZE; unsigned long numrecs = NUMRECS; unsigned long filebytes; unsigned long readrate, writerate; #ifdef TIME double starttime1, starttime2; double writetime, readtime; double totaltime; #endif #ifdef MSDOS buffer = (char *) calloc(1, MAXBUFFERSIZE); #endif if (!auto_mode) { printf("\n\tIOZONE: Performance Test of Sequential File I/O -- %s\n", THISVERSION); printf("\t\tBy Bill Norcott\n\n"); } strcpy(filename,default_filename); switch (argc) { case 1: /* no args, take all defaults */ printf(USAGE); break; case 2: /* <megabytes|filename> */ i = atoi(argv[1]); if (i) { megabytes = i; } else { /* 'Auto mode' will be enabled if the first command line argument is the word 'auto'. This will trigger a series of tests */ if ( (strcmp(argv[1], "auto") == 0) || (strcmp(argv[1], "AUTO") == 0) ) { auto_mode = 1; auto_test(); printf("Completed series of tests\n"); exit(0); } else { auto_mode = 0; } strcpy(filename,argv[1]); } break; case 3: /* <megabytes> <reclen|filename> */ megabytes = atoi(argv[1]); if (atoi(argv[2])) { reclen = atoi(argv[2]); } else { strcpy(filename,argv[2]); } break; case 4: /* <megabytes> <reclen> <filename> */ megabytes = atoi(argv[1]); reclen = atoi(argv[2]); strcpy(filename,argv[3]); break; default: printf(USAGE); exit(1); } if (!auto_mode) { printf("\tSend comments to:\tBill.Norcott@nuo.mts.dec.com\n\n"); } filesize = megabytes*1024*1024; numrecs = filesize/reclen; if (reclen > MAXBUFFERSIZE) { printf("Error: Maximum record length is %d bytes\n", MAXBUFFERSIZE); exit(1); } if (reclen < MINBUFFERSIZE) { printf("Error: Minimum record length is %d bytes\n", MINBUFFERSIZE); exit(1); } if (!auto_mode) { printf("\tIOZONE writes a %ld Megabyte sequential file consisting of\n", megabytes); printf("\t%ld records which are each %ld bytes in length.\n", numrecs, reclen); printf("\tIt then reads the file. It prints the bytes-per-second\n"); printf("\trate at which the computer can read and write files.\n\n"); printf("\nWriting the %ld Megabyte file, '%s'...", megabytes, filename); } if((fd = creat(filename, 0640))<0){ printf("Cannot create temporary file\n"); exit(1); } #ifdef TIME starttime1 = time_so_far(); #endif for(i=0; i<numrecs; i++){ #ifndef DEBUG_ME if(write(fd, buffer, (unsigned) reclen) != reclen) { printf("Error writing block %d\n", i); exit(1); } #endif } #ifdef TIME writetime = time_so_far() - starttime1; if (!auto_mode) { printf("%f seconds", writetime); } #endif close(fd); #if defined (VMS) if((fd = open(filename, O_RDONLY, 0640))<0){ printf("Cannot open temporary file for read\n"); exit(1); } #elif defined(MSDOS) if((fd = open(filename, O_RDONLY, 0640))<0){ printf("Cannot open temporary file for read\n"); exit(1); } #else if((fd = open(filename, O_RDONLY))<0){ printf("Cannot open temporary file for read\n"); exit(1); } #endif /*start timing*/ #if defined(NOTIME) printf("start timing\n"); #endif if (!auto_mode) { printf("\nReading the file..."); } starttime2 = time_so_far(); for(i=0; i<numrecs; i++) { #ifndef DEBUG_ME if(read(fd, buffer, (unsigned) reclen) != reclen) { printf("Error reading block %d\n", i); exit(1); } #endif } #ifdef NOTIME printf("stop timing\n"); #endif #ifdef TIME readtime = time_so_far() - starttime2; if (!auto_mode) { printf("%f seconds\n", readtime); } #ifdef DEBUG_ME readtime = 1; writetime = 1; #endif if(readtime!=0) { filebytes = numrecs*reclen; readrate = (unsigned long) ((double) filebytes / readtime); writerate = (unsigned long) ((double) filebytes / writetime); if (auto_mode) { printf(CONTROL_STRING1, megabytes, reclen, writerate, readrate); } else { printf("\nIOZONE performance measurements:\n"); printf("\t%ld bytes/second for writing the file\n", writerate); printf("\t%ld bytes/second for reading the file\n", readrate); totaltime = readtime + writetime; if (totaltime < TOOFAST) { goodmegs = (TOOFAST/totaltime)*2*megabytes; printf("\nThe test completed too quickly to give a good result\n"); printf("You will get a more precise measure of this machine's\n"); printf("performance by re-running IOZONE using the command:\n"); printf("\n\tiozone %ld ", goodmegs); printf("\t(i.e., file size = %ld megabytes)\n", goodmegs); } } } else { goodrecl = reclen/2; printf("\nI/O error during read. Try again with the command:\n"); printf("\n\tiozone %ld %ld ", megabytes, goodrecl); printf("\t(i.e. record size = %ld bytes)\n", goodrecl); } #endif close(fd); #ifndef VMS unlink(filename); /* delete the file */ /*stop timer*/ #endif #ifdef MSDOS free(buffer); /* deallocate the memory */ #endif #ifdef VMS return SS$_NORMAL; #else return 0; #endif } /****************************************************************** AUTO TEST -- perform series of tests and tabulate results ******************************************************************/ void auto_test() { int megsi, recszi; char megs[10]; char recsz[10]; int i,j; int argc = 3; char *argv[3]; printf(CONTROL_STRING2, "MB", "reclen", "bytes/sec written", "bytes/sec read"); argv[0] = "IOzone auto-test"; argv[1] = megs; argv[2] = recsz; for(i=0,megsi=1;i<5;i++,megsi*=2) { sprintf(megs, "%d", megsi); for (j=0,recszi=512;j<5;j++,recszi*=2) { sprintf(recsz, "%d", recszi); main(argc, argv); } } } static double time_so_far() { #if defined(VMS) /* * 5/17/91 Bill Norcott V1.07 -- use time() for VMS The times() function in VMS returns proc & user CPU time in 10-millisecond ticks. Instead, use time() which lacks the precision but gives clock time in seconds. */ return (double) time(NULL); #elif defined(SysVtime) int val; struct tms tms; if ((val = times(&tms)) == -1) perror("times"); return ((double) val) / ((double) CLK_TCK); #elif defined(MSDOS) return ((double) clock()) / ((double) CLK_TCK); #else struct timeval tp; if (gettimeofday(&tp, (struct timezone *) NULL) == -1) perror("gettimeofday"); return ((double) (tp.tv_sec)) + (((double) tp.tv_usec) / 1000000.0); #endif }