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
}