/*
 * gcc -O2 -o page-cache-test page-cache-test.c 
 * ./signal-echo 1024 10
 */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>
#include <fcntl.h>
#include <errno.h>

#define	BUFFER_SIZE	8192

int
main(int argc, char **argv)
{
	int		fd;
	size_t	file_size;
	size_t	file_off;
	int		num_loops;
	char	buffer[8192];
	double	duration;

	/* used to measure time */
	struct timeval time_start;
	struct timeval time_end;

	/* should get three arguments */
	if (argc != 3)
	{
		printf("invalid number of arguments\n");
		exit(1);
	}

	/* in bytes */
	file_size = atoi(argv[1]) * 1024L * 1024L;
	num_loops = atoi(argv[2]);

	printf("file_size = %ld\n", file_size);
	printf("num_loops = %d\n", num_loops);

	fd = open("page-cache.test", O_CREAT | O_RDWR, S_IRWXU);

	if (fd < 0)
		exit(1);

	file_off = 0;
	while (file_off < file_size)
	{
		int	off = 0;

		for (int i = 0; i < BUFFER_SIZE; i++)
			buffer[i] = (char) rand();

		while (off < BUFFER_SIZE)
		{
			int r = write(fd, &buffer[off], BUFFER_SIZE - off);

			if (r > 0)
			{
				off += r;
				continue;
			}

			/* just retry */
			if (errno == EAGAIN)
				continue;

			printf("fd %d errno = %d: %m\n", fd, errno);
			exit(1);
		}

		file_off += BUFFER_SIZE;
	}

	fsync(fd);

	/* read file from page cache sequentially */
	for (int nloop = 0; nloop < num_loops; nloop++)
	{
		/* remember when we started */
		gettimeofday(&time_start, NULL);

		lseek(fd, 0, SEEK_SET);
		file_off = 0;
		while (file_off < file_size)
		{
			/* retry */
			if (read(fd, buffer, BUFFER_SIZE) != BUFFER_SIZE)
				continue;

			file_off += BUFFER_SIZE;
		}

		/* distance from time_start s*/
		gettimeofday(&time_end, NULL);
		duration = (time_end.tv_sec - time_start.tv_sec) * 1000000 +
				   (time_end.tv_usec - time_start.tv_usec);

		printf("SEQUENTIAL loop %d duration %.2f (%.2f)\n", nloop, duration, file_size / (1024.0 * 1024.0 * 1024.0) / (duration / 1000000.0));
	}

	/* read file from page cache random */
	for (int nloop = 0; nloop < num_loops; nloop++)
	{
		int		nbuffs = (file_size / BUFFER_SIZE);
		int		nreads = nbuffs;

		/* remember when we started */
		gettimeofday(&time_start, NULL);

		while (nreads > 0)
		{
			file_off = BUFFER_SIZE * (rand() % nbuffs);
			lseek(fd, file_off, SEEK_SET);

			/* retry */
			if (read(fd, buffer, BUFFER_SIZE) != BUFFER_SIZE)
				continue;

			nreads--;
		}

		/* distance from time_start s*/
		gettimeofday(&time_end, NULL);
		duration = (time_end.tv_sec - time_start.tv_sec) * 1000000 +
				   (time_end.tv_usec - time_start.tv_usec);

		printf("RANDOM loop %d duration %.2f (%.2f)\n", nloop, duration, file_size / (1024.0 * 1024.0 * 1024.0) / (duration / 1000000.0));
	}

	close(fd);

	return 0;
}
