/*
*
*   "I/O Stone" Benchmark Program
*
*   Written by: Arvin Park (park@midas.ucdavis.edu) and
*            Jeff Becker (becker@iris.ucdavis.edu)
*            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 becker@iris.ucdavis.edu. 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);
	}
      }
    }
  }
}


