[comp.benchmarks] IOZONE V1.07 portable benchmark of sequential file I/O

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
}