[comp.sources.misc] v20i019: iozone - sequential file I/O benchmark for UNIX, VMS & DOS, Part01/01

norcott@databs.enet.dec.com (Bill Norcott) (05/25/91)

Submitted-by: Bill Norcott <norcott@databs.enet.dec.com>
Posting-number: Volume 20, Issue 19
Archive-name: iozone/part01
Supersedes: iozone: Volume 17, Issue 65

IOzone is an I/O benchmark which I have written.  It is a performance
test of sequential file I/O.  IOzone calculates the speed at which 
your system reads and writes files, and prints the rate in bytes per 
second.  This is a portable benchmark, and has been tested on
VAX/VMS, MS-DOS, AIX, SCO UNIX, SUNOS, ULTRIX, etc.  I would appreciate
feedback on any changes needed to make it compile on Hewlett Packard,
Apple, Interactive Unix, etc -- systems not already on my list.

Attached is IOzone V1.08.  This version includes auto test mode
which performs a series of tests on file sizes from 1-16 MB and
record lengths from 512 to 8192 bytes.  It is invoked by the 
typing: iozone auto   from the the shell.  This version also
fixes auto mode when compiled with Borland Turbo C on MS-DOS,
and lets you compile IOzone on SUNOS without having to use
-Dnolimits (the source code now compiles on SUNOS with the
simple  cc -o iozone iozone.c).

Bill Norcott

------------------------   CUT HERE -------------------------
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# Contents:  iozone.c
# Wrapped by kent@sparky on Fri May 24 15:40:33 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo '          "shar: End of archive."'
if test -f 'iozone.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'iozone.c'\"
else
  echo shar: Extracting \"'iozone.c'\" \(14433 characters\)
  sed "s/^X//" >'iozone.c' <<'END_OF_FILE'
X/*
X*	"IO Zone" Benchmark Program
X*
X*	Author:	Bill Norcott (Bill.Norcott@nuo.mts.dec.com)
X*		4 Dunlap Drive
X*		Nashua, NH  03060
X*
X*  "Copyright 1991,   William D. Norcott
X*  License to freely use and distribute this software is hereby granted 
X*  by the author, subject to the condition that this copyright notice 
X*  remains intact.  The author retains the exclusive right to publish 
X*  derivative works based on this work, including, but not limited, 
X*  to revised versions of this work"
X*
X* "This test writes a 4 MEGABYTE sequential file in 512 byte chunks, then
X* rewinds it  and reads it back.  [The size of the file should be
X* big enough to factor out the effect of any disk cache.]
X*       
X* The file is written (filling any cache buffers), and then read.  If the
X* cache is >= 4 MB, then most if not all the reads will be satisfied from
X* the cache.  However, if it is less than or equal to 2 MB, then NONE of
X* the reads will be satisfied from the cache.  This is becase after the 
X* file is written, a 2 MB cache will contain the upper 2 MB of the test
X* file, but we will start reading from the beginning of the file (data
X* which is no longer in the cache)
X*
X* In order for this to be a fair test, the length of the test file must
X* be AT LEAST 2X the amount of disk cache memory for your system.  If
X* not, you are really testing the speed at which your CPU can read blocks
X* out of the cache (not a fair test)
X*       
X* IOZONE does NOT test the raw I/O speed of your disk or system.  It
X* tests the speed of sequential I/O to actual files.  Therefore, this
X* measurement factors in the efficiency of you machines file system,
X* operating system, C compiler, and C runtime library.  It produces a 
X* measurement which is the number of bytes per second that your system
X* can read or write to a file.  
X*
X* For V1.06, IOZONE adds the "auto test" feature.  This is activated
X* by the command:  'iozone auto' .  The auto test runs IOZONE repeatedly  
X* using record sizes from 512 to 8192 bytes, and file sizes from 1 to 16
X* megabytes.  It creates a table of results.
X*       
X* For V1.06, IOZONE lets you specify the number of file system sizes and      
X* record lengths to test when using auto mode.  Define the constants
X* MEGABYTES_ITER_LIMIT and RECLEN_ITER_LIMIT as seen below      
X*       
X* This program has been ported and tested on the following computer
X* operating systems:
X*
X*	AIX Version 3 release 1 (compile: cc -Dunix -o iozone iozone.c)
X*	MS-DOS 3.3		(Turbo C, Turbo C++, Microsoft C)
X*	OSF/1
X*	SCO Unix System V release 3.2 version 2
X*	SUNOS 4.0.3, SUNOS 4.1.1
X*	Ultrix V4.1 
X*	VAX/VMS V5.4	**	 
X*	XENIX 3.2
X*       
X*   ** for VMS, define iozone as a foreign command via this DCL command:       
X*
X*	$IOZONE :== $SYS$DISK:[]IOZONE.EXE      
X*
X*	this lets you pass the command line arguments to IOZONE
X*       
X* Acknowledgements to the following persons for their feedback on IOzone:       
X*
X* Andy Puchrik, Michael D. Lawler, Krishna E. Bera, Sam Drake, John H. Hartman, 
X* Ted Lyszczarz, Bill Metzenthen
X*       
X* --- MODIFICATION HISTORY:
X*
X*   3/7/91 William D. Norcott (Bill.Norcott@nuo.mts.dec.com)
X*	    created
X*
X*   3/22/91 Bill Norcott    	tested on OSF/1 ... it works
X*
X*   3/24/91 Bill Norcott        V1.02 -- use calloc in TURBOC to
X*					fix bug with their malloc
X*
X*   3/25/91 Bill Norcott        V1.03 -- add ifdef for XENIX
X*					
X*   3/27/91 Bill Norcott	V1.04 -- Includes for SCO UNIX
X*					
X*   4/26/91 Bill Norcott	V1.05 -- support AIX and SUNos, check
X*					length of read() and write()
X*   4/26/91 Bill Norcott	V1.06 -- tabulate results of a series 
X*					of tests
X*   5/17/91 Bill Norcott	V1.07 -- use time() for VMS
X*   5/20/91 Bill Norcott	V1.08 -- use %ld for Turbo C and
X*					use #ifdef sun to bypass
X*					inclusion of limits.h
X*/
X
X/******************************************************************
X
X    INCLUDE FILES (system-dependent)
X
X******************************************************************/
X#ifdef	__MSDOS__		/* Turbo C define this way for PCs... */
X#define	MSDOS			/* Microsoft C defines this */
X#endif
X/* VMS and MS-DOS both have ANSI C compilers and use rand()/srand() */
X#ifdef	VMS_POSIX
X#undef   VMS
X#define	ANSI_RANDOM	1
X#endif
X#ifdef	MSDOS
X#define	ANSI_RANDOM	1
X#endif
X
X/* incl definitions of O_* flags for XENIX */
X#ifdef M_XENIX
X#include <fcntl.h>
X#endif
X/* SCO Unix System V */
X#ifdef M_UNIX
X#include <sys/types.h>
X#include <sys/fcntl.h>
X#endif
X
X#if defined(VMS)
X#define	ANSI_RANDOM	1
X#include    <math.h>
X#include    <unixio.h>
X#include    <ssdef.h>
X#include    <file.h>
X#include    <time.h>
X
X#elif defined(MSDOS)
X#include <fcntl.h>
X#include <time.h>
X#elif defined(unix)
X#include <sys/file.h>
X#ifndef NULL
X#define NULL 0
X#endif
X/* 
Xdefine nolimits if your system has no limits.h.  Sun's don't but I
Xtake care of this explicitly beginning with V1.08 of IOzone.
X*/
X#ifdef sun
X#define nolimits
X#define BSDtime
X#endif
X#ifndef nolimits
X#include <limits.h>
X#endif
X#endif
X/* for systems with System V-style time, define SysVtime */
X#ifdef M_SYSV
X#define SysVtime
X#endif
X
X#ifdef SysVtime
X#include <sys/times.h>
X#include <sys/param.h>
X#ifndef CLK_TCK
X#define CLK_TCK HZ
X#endif
X#endif
X/* for systems with BSD style time, define BSDtime */
X#ifdef bsd4_2
X#define BSDtime
X#endif
X#ifdef BSDtime
X#include <sys/time.h>
X#endif
X
X
X/******************************************************************
X
X    DEFINED CONSTANTS
X
X******************************************************************/
X#define MEGABYTES 1			/* number of megabytes in file */
X#define RECLEN 512			/* number of bytes in a record */
X#define FILESIZE (MEGABYTES*1024*1024)	/*size of file in bytes*/
X#define NUMRECS FILESIZE/RECLEN		/* number of records */
X#define MAXBUFFERSIZE 16*1024		/*maximum buffer size*/
X#define MINBUFFERSIZE 128
X#define TOOFAST 10
X#define USAGE  "\tUsage:\tiozone [megabytes] [record_length_in_bytes] \
X[[path]filename]\n\n"
X#define THISVERSION "V1.08"
X
X/* Define only one of the following two.*/
X/*#define NOTIME		define if no time function in library*/
X#define TIME				/*Use time(2)function*/
X#define MAXNAMESIZE 1000                /* max # of characters in filename */
X#define CONTROL_STRING1 "\t%-8ld%-8ld%-20ld%-20ld\n"
X#define CONTROL_STRING2 "\t%-8s%-8s%-20s%-20s\n"
X/* 
X    For 'auto mode', these defines determine the number of iterations
X    to perform for both the file size and the record length.
X    I.e., if MEGABYTES_ITER_LIMIT = 5 use 1, 2, 4, 8 & 16 megabyte files
X    if RECLEN_ITER_LIMIT = 5 use 512, 1024, 2048, 4096 & 8192 byte records
X*/ 
X#define MEGABYTES_ITER_LIMIT 5
X#define RECLEN_ITER_LIMIT 5
X
X/******************************************************************
X
X    FUNCTION DECLARATIONS
X
X******************************************************************/
Xvoid auto_test();		/* perform automatic test series */
Xstatic double time_so_far();	/* time since start of program */
X/******************************************************************
X
X    GLOBAL VARIABLES
X
X******************************************************************/
Xint auto_mode;
X
X/******************************************************************
X
X    MAIN -- entry point
X
X******************************************************************/
Xmain(argc,argv) 
X      int argc;
X     char *argv[];
X{
Xint fd;
Xchar filename [MAXNAMESIZE];            /* name of temporary file */
Xchar *default_filename="iozone.tmp"; /*default name of temporary file*/
X
X#ifdef	MSDOS
Xchar *buffer; 
X#else
Xchar buffer [MAXBUFFERSIZE];		/*a temporary data buffer*/
X#endif
Xint i, status;
Xunsigned long megabytes = MEGABYTES, goodmegs;
Xunsigned long reclen = RECLEN, goodrecl;
Xunsigned long filesize = FILESIZE;
Xunsigned long numrecs = NUMRECS;
Xunsigned long filebytes;
Xunsigned long readrate, writerate;
X#ifdef TIME
X double starttime1, starttime2;
X double writetime, readtime;
X double totaltime;
X
X#endif
X
X#ifdef MSDOS
X  buffer = (char *) calloc(1, MAXBUFFERSIZE);
X#endif
X
X  if (!auto_mode)
X  {
X    printf("\n\tIOZONE: Performance Test of Sequential File I/O  --  %s\n",
X  		THISVERSION);
X    printf("\t\tBy Bill Norcott\n\n");
X  }
X  strcpy(filename,default_filename);
X  switch (argc) {
X    case 1:   	/* no args, take all defaults */
X  	printf(USAGE);
X	break;
X    case 2:     /* <megabytes|filename> */
X	i = atoi(argv[1]); if (i) {
X	  megabytes = i;
X	} else {
X/*
X'Auto mode' will be enabled if the first command line argument is
Xthe word 'auto'.  This will trigger a series of tests
X*/
X	  if ( (strcmp(argv[1], "auto") == 0) || 
X	       (strcmp(argv[1], "AUTO") == 0) )
X	  {
X	    auto_mode = 1;
X	    auto_test();
X	    printf("Completed series of tests\n");
X	    exit(0);
X	  } else {
X	    auto_mode = 0;
X	  }
X	  strcpy(filename,argv[1]);
X        }
X	break;
X    case 3:     /* <megabytes> <reclen|filename> */
X	megabytes = atoi(argv[1]);
X	if (atoi(argv[2])) {
X	  reclen = atoi(argv[2]);
X	} else {
X	  strcpy(filename,argv[2]);
X	}
X	break;
X    case 4:     /* <megabytes> <reclen> <filename> */
X	megabytes = atoi(argv[1]);
X	reclen = atoi(argv[2]);
X	strcpy(filename,argv[3]);
X	break;
X    default:
X      printf(USAGE);
X      exit(1);
X
X  }
X  if (!auto_mode)
X  {
X    printf("\tSend comments to:\tBill.Norcott@nuo.mts.dec.com\n\n");
X  }
X  filesize = megabytes*1024*1024;
X  numrecs = filesize/reclen;
X  if (reclen >  MAXBUFFERSIZE) {
X	printf("Error: Maximum record length is %d bytes\n", MAXBUFFERSIZE);
X        exit(1);
X	}
X  if (reclen < MINBUFFERSIZE) {
X	printf("Error: Minimum record length is %d bytes\n", MINBUFFERSIZE);
X        exit(1);
X	}
X  if (!auto_mode)
X  {
X    printf("\tIOZONE writes a %ld Megabyte sequential file consisting of\n",
X	megabytes);
X    printf("\t%ld records which are each %ld bytes in length.\n",
X	numrecs, reclen);
X    printf("\tIt then reads the file.  It prints the bytes-per-second\n");
X    printf("\trate at which the computer can read and write files.\n\n");
X    printf("\nWriting the %ld Megabyte file, '%s'...", megabytes, filename);
X  }  
X	if((fd = creat(filename, 0640))<0){
X		printf("Cannot create temporary file\n");
X		exit(1);
X	}
X#ifdef TIME
X	starttime1 = time_so_far();
X#endif
X	for(i=0; i<numrecs; i++){
X#ifndef DEBUG_ME
X		if(write(fd, buffer, (unsigned) reclen) != reclen)
X		    {
X		    printf("Error writing block %d\n", i);
X		    exit(1);
X		    }
X#endif
X	}
X
X#ifdef TIME
X	writetime = time_so_far() - starttime1;
X	if (!auto_mode)
X	    {
X	    printf("%f seconds", writetime);
X	    }
X#endif
X	close(fd);
X#if defined (VMS)
X	if((fd = open(filename, O_RDONLY, 0640))<0){
X		printf("Cannot open temporary file for read\n");
X		exit(1);
X	}
X#elif defined(MSDOS)
X	if((fd = open(filename, O_RDONLY, 0640))<0){
X		printf("Cannot open temporary file for read\n");
X		exit(1);
X	}
X#else
X	if((fd = open(filename, O_RDONLY))<0){
X		printf("Cannot open temporary file for read\n");
X		exit(1);
X       }
X#endif
X
X
X			/*start timing*/
X#if defined(NOTIME)
X	printf("start timing\n");
X#endif
X	if (!auto_mode)
X	{
X	    printf("\nReading the file...");
X	}
X	starttime2 = time_so_far();
X   for(i=0; i<numrecs; i++) {
X#ifndef DEBUG_ME
X	if(read(fd, buffer, (unsigned) reclen) != reclen)
X	{
X		printf("Error reading block %d\n", i);
X		exit(1);
X	}
X#endif
X    }
X#ifdef NOTIME
X	printf("stop timing\n");
X#endif
X#ifdef TIME
X	readtime = time_so_far() - starttime2;
X	if (!auto_mode)
X	{
X	    printf("%f seconds\n", readtime);
X	}
X#ifdef DEBUG_ME
X	readtime = 1;
X	writetime = 1;
X#endif
X	if(readtime!=0)
X	{   
X	    filebytes = numrecs*reclen;
X	    readrate = (unsigned long) ((double) filebytes / readtime);
X	    writerate = (unsigned long) ((double) filebytes / writetime);
X	    if (auto_mode)
X	    {
X 		printf(CONTROL_STRING1,
X			megabytes, 
X			reclen,
X			writerate,
X			readrate);
X					
X	    } else {
X		printf("\nIOZONE performance measurements:\n");
X		printf("\t%ld bytes/second for writing the file\n", writerate);
X		printf("\t%ld bytes/second for reading the file\n", readrate);
X		totaltime = readtime + writetime;
X		if (totaltime < TOOFAST) 
X		{
X		    goodmegs = (TOOFAST/totaltime)*2*megabytes;
X		    printf("\nThe test completed too quickly to give a good result\n");
X		    printf("You will get a more precise measure of this machine's\n");
X		    printf("performance by re-running IOZONE using the command:\n");
X		    printf("\n\tiozone %ld ", goodmegs);
X		    printf("\t(i.e., file size = %ld megabytes)\n", goodmegs);
X		}
X	    }
X	} else {
X	    goodrecl = reclen/2;
X	    printf("\nI/O error during read.  Try again with the command:\n");
X	    printf("\n\tiozone %ld %ld ", megabytes,  goodrecl);
X	    printf("\t(i.e. record size = %ld bytes)\n",  goodrecl);
X	}
X#endif
X    close(fd);
X#ifndef VMS
X    unlink(filename);	/* delete the file */
X					/*stop timer*/
X#endif
X#ifdef	MSDOS
X	free(buffer);		/* deallocate the memory */
X#endif
X#ifdef VMS
Xreturn SS$_NORMAL;
X#else
Xreturn 0;
X#endif
X}
X/******************************************************************
X
X    AUTO TEST -- perform series of tests and tabulate results
X
X******************************************************************/
Xvoid auto_test()
X{
X    int megsi, recszi;
X    char megs[10]; 
X    char recsz[10];
X    int i,j;
X    int argc = 3;
X    char *argv[3];
X    
X    printf(CONTROL_STRING2,
X	"MB", 
X	"reclen",
X	"bytes/sec written",
X	"bytes/sec read");
X    argv[0] = "IOzone auto-test";
X    argv[1] = megs;
X    argv[2] = recsz;
X/*
XStart with file size of 1 megabyte and repeat the test MEGABYTES_ITER_LIMIT
Xtimes.  Each time we run, the file size is doubled
X*/
X    for(i=0,megsi=1;i<MEGABYTES_ITER_LIMIT;i++,megsi*=2) 
X    {
X	sprintf(megs, "%d", megsi);
X/*
XStart with record size of 512 bytes and repeat the test RECLEN_ITER_LIMIT
Xtimes.  Each time we run, the record size is doubled
X*/
X	for (j=0,recszi=512;j<RECLEN_ITER_LIMIT;j++,recszi*=2)
X	{
X	    sprintf(recsz, "%d", recszi);
X	    main(argc, argv);
X	}
X    }
X}
X
Xstatic double
Xtime_so_far()
X{
X#if defined(VMS)
X/* 
X*   5/17/91 Bill Norcott	V1.07 -- use time() for VMS
XThe times() function in VMS returns proc & user CPU time in 10-millisecond
Xticks.  Instead, use time() which lacks the precision but gives clock
Xtime in seconds.
X*/
X
X  return (double) time(NULL);
X
X#elif defined(SysVtime)
X  int        val;
X  struct tms tms;
X
X  if ((val = times(&tms)) == -1)
X    perror("times");
X
X  return ((double) val) / ((double) CLK_TCK);
X#elif defined(MSDOS)
X  return ((double) clock()) / ((double) CLK_TCK);
X#else
X  struct timeval tp;
X
X  if (gettimeofday(&tp, (struct timezone *) NULL) == -1)
X    perror("gettimeofday");
X  return ((double) (tp.tv_sec)) +
X    (((double) tp.tv_usec) / 1000000.0);
X#endif
X}
END_OF_FILE
  if test 14433 -ne `wc -c <'iozone.c'`; then
    echo shar: \"'iozone.c'\" unpacked with wrong size!
  fi
  # end of 'iozone.c'
fi
echo shar: End of archive.
exit 0
exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.