/*
*
*   "I/O Stone" Benchmark Program
*
*   Written by: Arvin Park ([email protected]) and
*            Jeff Becker ([email protected])
*            Division of Computer Science
*            University of California, Davis
*            Davis CA 95616
*            (916) 752-5183
*
*   Version C/II
*   Date: 06/27/90
*
*   Defines: If your version of "C" does not include an ftime()
*            function or the C library time.h, define NOTIME.
*            Use a stopwatch to measure elapsed wall time.
*            Divide 2,000,000 by the elapsed time to get
*            the correct number of iostones/second.
*
*   To compile:   cc -O io.c -o io
*
*   Note:    [1] This program should be run without other processes
*            competing for system resources. Run it in the dead of
*            night if you have to.
*
*            [2] This program uses 5 megabytes of disk space. Make
*            sure that at least this much space is available on
*            your file system before you run the program.
*
*   Results: If you get results from a new (machine/operating
*            system/disk controller and drive) combination, please
*            send them to [email protected]. Please include
*            complete information on the machine type, operating
*            system, version, disk controller, and disk drives.
*            Also make a note of any system modifications that
*            have been performed.
*/

#include <stdio.h>

#define BFLUSH_FILE_SIZE (512L*1024L)   /*size of files used to flush buffers*/
#define NBFLUSH_FILES 8                 /*number of files used to flush buffers*/
#define NBLOCKSIZES 9                   /*number of different block sizes*/
#define SEED 34710373L                  /*random number generator seed*/
#define CONST 500000L                   /*iostone normalization constant*/
#define ITER 4                          /*number of iterations of the code*/
#define BUFFERSIZE (16L*1024L)          /*size of temporary buffer*/

/* define only one of the next three defines */
/*#define NOTIME                /* Define if no time function in library */
#define TIME                    /* Use UNIX time function */
/*#define NON_UNIX_TIME         /* Use if a non-unix system */

#define NSETS 4                         /*number of sets of files*/
#define SET_SIZE 99                     /*number of files in each set*/
#define FNAMELEN 20                     /*maximum file name length*/

char *malloc();
char tmp[FNAMELEN];                     /*a temporary string*/
char *files[NSETS][SET_SIZE];           /*array of file names*/
char *buf_flush_files[NBFLUSH_FILES];   /*array of names of files to flush*/
                                       /*system buffers*/
char buffer[BUFFERSIZE];                /*a temporary buffer*/

long int nbytes;                        /*number of bytes transfered*/
int fd;                                 /*file descriptor*/
int i,j,k;                              /*counter variables*/
long bsize[NBLOCKSIZES];                /*array for different block sizes*/
int bfreq[NBLOCKSIZES];                 /*number of accesses for each block*/

#ifdef TIME
#include <sys/types.h>
#include <sys/timeb.h>
struct timeb before;
struct timeb after;
int sec;
int msec;
#endif

#ifdef NON_UNIX_TIME
long starttime;
long totaltime;
#endif

main() {
 init();
                                       /*start timing*/
#ifdef NOTIME
 printf("start timing\n");
#endif
#ifdef TIME
 ftime(&before);
#endif
#ifdef NON_UNIX_TIME
 starttime = time(0);
#endif

 for(k=0; k<ITER; k++)                 /*perform string of file operations*/
   readswrites();
                                       /*stop timimg*/
#ifdef NOTIME
 printf("stop timing\n");
#endif
#ifdef TIME
 ftime(&after);
 sec = after.time - before.time;
 msec = after.millitm - before.millitm;
 if (msec < 0) {                       /*adjust if fractional time < 0*/
   sec -= 1;
   msec += 1000;
 }
 printf("Total elapsed time is %d seconds and %d milliseconds\n",sec,msec);
 if (sec!=0 || msec!=0)
   printf("This machine benchmarks at %.0f iostones/second\n",
          ((float)(CONST*ITER)/((float) sec + (float) msec/1000)) + 0.5);
#endif
#ifdef NON_UNIX_TIME
 totaltime = time(0) - starttime;
 printf("Total elapsed time is %ld seconds\n",totaltime);
 if (totaltime!=0)
   printf("This machine benchmarks at %.0f iostones/second\n",
          ((float)(CONST*ITER)/((float) totaltime)) + 0.5);
#endif
 for (i=0;i<NSETS;i++)
   for (j=0;j<SET_SIZE;j++)
     unlink(files[i][j]);              /*remove files*/
 for (k=0;k<NBFLUSH_FILES;k++)
   unlink(buf_flush_files[k]);
}

init() {

 int this_set;                         /*mark the file set (0..NSETS-1)*/
 int bcount;                           /*counter to track #spacer files*/
 int fcount;                           /*a counter to tell where to create*/
                                       /*files to flush buffer cache and*/
                                       /*spread other files across disk*/
 bsize[0]=256; bfreq[0]=128;
 bsize[1]=512; bfreq[1]=64;
 bsize[2]=1024; bfreq[2]=64;
 bsize[3]=2048; bfreq[3]=64;
 bsize[4]=4096; bfreq[4]=32;           /*set file block sizes and*/
 bsize[5]=8192; bfreq[5]=32;           /*access frequencies*/
 bsize[6]=16384; bfreq[6]=8;
 bsize[7]=32768; bfreq[7]=2;
 bsize[8]=65536; bfreq[8]=2;

 k=0;                                  /*set up files*/
 bcount=0;
 fcount=0;
 for(i=0;i<NBLOCKSIZES;i++) {
   for(j=0;j<bfreq[i];j++) {
     if (i<NBLOCKSIZES-1)
       this_set = j%NSETS;
     else
       this_set = (j+2)%NSETS;
     sprintf(tmp,"%0d_%0d",i,j);       /*create filename*/
     files[this_set][k] = malloc(1+strlen(tmp));
     if (!files[this_set][k]) {
       printf("Could not allocate string for filename\n");
       exit(1);
     }
     strcpy(files[this_set][k],tmp);
     initfile(tmp,bsize[i]);
     if (i < NBLOCKSIZES-1 && this_set == NSETS-1) k++;
     if (bcount < NBFLUSH_FILES && fcount%44 == 0) {
       sprintf(tmp,"%bf_%0d",bcount);       /*create spacer file*/
       buf_flush_files[bcount] = malloc(1+strlen(tmp));
       if (!buf_flush_files[bcount]) {
         printf("Could not allocate string for filename\n");
         exit(1);
       }
       strcpy(buf_flush_files[bcount],tmp);
       initfile(tmp,BFLUSH_FILE_SIZE);
       bcount++;
     }
     fcount++;
   }
 }

 for(i=0;i<NBFLUSH_FILES;i++) {        /*read spacer files to flush buffers*/
   if ((fd = open(buf_flush_files[i],2))<0) {
     printf("error opening buffer flush file\n");
     exit(1);
   }
   lseek(fd,0L,0);
   k = BFLUSH_FILE_SIZE/BUFFERSIZE;
   for(j=0;j<k;j++) {
     if((nbytes = read(fd,buffer,BUFFERSIZE))<0) {
       printf("Error reading buffer flush file\n");
       exit(1);
     }
   }
   close(fd);
 }

#ifdef NON_UNIX_TIME
 srand(SEED);
#else
 srandom(SEED);                        /*initialize random number generator*/
#endif                                  /*and order files in a random*/
 for(i=0;i<NSETS;i++) {                /*permutation for reading/writing*/
   for(j=SET_SIZE;j>0;j--) {
     k=my_rand(j);
     strcpy(tmp,files[i][j-1]);
     strcpy(files[i][j-1],files[i][k]);
     strcpy(files[i][k],tmp);
   }
 }
}

my_rand(max)

    int max;

{
#ifdef NON_UNIX_TIME
 return rand()%max;
#else
 return random()%max;
#endif
}


initfile(fname, fsize)

    char *fname;
    long fsize;

{                                       /*create a temporary file*/
 FILE *fs;
 int block, num_blocks;

 if((fs=fopen(fname,"w"))==NULL){
   printf("init: Cannot create temporary file\n");
   exit(1);
 }
 rewind(fs);                           /*write initial portion of file*/
 if (fsize > BUFFERSIZE) {
   num_blocks=fsize/BUFFERSIZE;
   for(block=0;block<num_blocks;block++) {
     if ((nbytes=fwrite(buffer,1,BUFFERSIZE,fs))<0) {
       printf("init: error writing block\n");
       exit(1);
     }
   }
 }
 else {
   if ((nbytes=fwrite(buffer,1,fsize,fs))<0) {
     printf("init: error writing block\n");
     exit(1);
   }
 }
 fclose(fs);
}

readswrites(){

 int xfer, num_xfer;                   /*to access buffer correct # times*/
 long xfer_amt;                        /*amount to transfer to/from buffer*/
 int fsize_index;                      /*file size index (0..8)*/
 int rnum;                             /*rand. num to choose read or write*/
 int rep1,rep2;                        /*indices to loop through each file*/
                                       /*set twice, and all sets three times*/
 for(rep1=0;rep1<3;rep1++) {           /*in order to achieve locality which*/
   for(i=0;i<NSETS;i++) {              /*is consistent with buffer cache data*/
     for(rep2=0;rep2<2;rep2++) {       /*of Ousterhout et al (1985)*/
       for(j=0;j<SET_SIZE;j++) {
         if ((fd = open(files[i][j],2))<0) {
           printf("readswrites: cannot open file\n");
           exit(1);
         }
         fsize_index = *(files[i][j]) -'0';     /*max xfer_amt = BUFFERSIZE*/
         if (bsize[fsize_index] >= BUFFERSIZE) {
           num_xfer = bsize[fsize_index]/BUFFERSIZE;
           xfer_amt = BUFFERSIZE;}
         else {
           num_xfer = 1;
           xfer_amt = bsize[fsize_index];}
         rnum = my_rand(3);
         if (rnum < 2) {               /*read:write = 2:1*/
           lseek(fd,0L,0);
           for (xfer=0; xfer<num_xfer; xfer++) {
             if((nbytes=read(fd,buffer,xfer_amt))<0) {
               printf ("readswrites: read error\n");
               exit(1);
             }
           }
         }
         else {
           lseek(fd,0L,0);
           for (xfer=0; xfer<num_xfer; xfer++) {
             if((nbytes=write(fd,buffer,xfer_amt))<0) {
               printf ("readswrites: write error\n");
               exit(1);
             }
           }
         }
         close(fd);
       }
     }
   }
 }
}