#define _GNU_SOURCE 1
#define _FILE_OFFSET_BITS 64

#include <assert.h>
#include <sys/time.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#define BUF_SIZE 131072

// Compile with: gcc iospeed.c -o iospeed -O3 -static

double tdiff(struct timeval *a, struct timeval *b)
{
	// Returns time in seconds
	double retval = (b->tv_sec - a->tv_sec) + (b->tv_usec - a->tv_usec) / 1000000.0;
	
	return retval;
}

int main(int argc, char *argv[])
{
	struct timeval t0, t1, t2;
	int rc= 0;
	int i=0;
	if(argc != 3)
	{
		fprintf(stderr,"usage: %s <filename> <size-in-MiB>\n", argv[0]);
		return 1;
	}
	int mbSize = atoi(argv[2]);
	assert(mbSize > 0);
	
	int fd = open(argv[1], O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE, 0644);
	assert(fd != -1);
	
	char *buf = NULL;
	
	rc = posix_memalign(&buf, 512, BUF_SIZE);
	assert(rc == 0);
	for(i=0; i<BUF_SIZE; i++)
	{
		buf[i] = i % 137;	// Give it some non-zero data
	}
	
	
	int numloops = mbSize * (1048576 / BUF_SIZE);
	
	rc = gettimeofday(&t0, NULL);
	assert(rc == 0);
	
	for (i=0; i<numloops; i++)
	{
		int towrite = BUF_SIZE;
		
		while(towrite > 0)
		{
			do {
				rc = write(fd, buf + (BUF_SIZE - towrite), towrite);
			} while ((rc == -1) && (errno == EINTR));
			
			assert(rc > 0);
			assert(rc <= towrite);
			if(rc != towrite)
			{
				fprintf(stderr,"Only wrote %d of %d bytes\n", rc, towrite);
			}
			towrite -= rc;
		}
	}
	rc = gettimeofday(&t1, NULL);
	assert(rc == 0);
	
	rc = fsync(fd);
	assert(rc == 0);
	
	rc = gettimeofday(&t2, NULL);
	assert(rc == 0);
	
	double tWrite = tdiff(&t0, &t1);
	double tSync  = tdiff(&t1, &t2);
	
	rc = posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED); // Drop from cache
	assert(rc == 0);
	
	rc = lseek(fd, 0, SEEK_SET);
	assert(rc == 0);
	
	rc = gettimeofday(&t0, NULL);
	assert(rc == 0);
	
	for (i=0; i<numloops; i++)
	{
		int toread = BUF_SIZE;
		
		while(toread > 0)
		{
			do {
				rc = read(fd, buf + (BUF_SIZE - toread), toread);
			} while ((rc == -1) && (errno == EINTR));
			
			assert(rc > 0);
			assert(rc <= toread);
			if(rc != toread)
			{
				fprintf(stderr,"Only read %d of %d bytes\n", rc, toread);
			}
			toread -= rc;
		}
	}
	rc = gettimeofday(&t1, NULL);
	assert(rc == 0);

	double tRead = tdiff(&t0, &t1);

	close(fd);
	
	printf("Size (MiB)\tWrite (MiB/s)\tRead (MiB/s)\n");
	printf("%d\t%.3f\t%.3f\n", mbSize, mbSize / (tWrite + tSync), mbSize / tRead);
	
	rc = unlink(argv[1]);
	assert(rc == 0);
	
	return 0;
}
