/*
 * $VER: MMUTest.c 1.0 (14.4.98) by Bernardo Innocenti
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>


/* Configuration parameters */

#define BUFSIZE 		(4*1024*1024)	/* Must be a power of two */
#define MMU_PAGE_SIZE	8192			/* Must be >= to your MMU page size */
#define CACHE_LINE_SIZE	32				/* Must be >= to your data cache line size  */
#define MAXITER 		33554432		/* (32M) Tune this to your CPU speed */



/* We need to do something that seems to be useful
 * in order to deceive clever optimizers.
 */
char a;

int main (void)
{
	unsigned int iter, index = 0;
	char *buf;
	clock_t start, end;
	float cache_miss_time;
	float mmu_miss_time;


	printf ("MMUTest reads sparse bytes from a big memory block\n");
	printf ("to encourage MMU cache misses. This will force the MMU\n");
	printf ("to fetch a new page descriptor from memoty almost every time.\n\n");

	printf ("MMUTest empirically estimates the MMU speed factor comparing\n");
	printf ("the speed difference between the test described above and\n");
	printf ("a similar test which accesses memory in a way which causes\n");
	printf ("data cache misses.\n\n");


	if (buf = malloc (BUFSIZE))
	{
		printf ("Running sequential test...      ");
		fflush (stdout);
		start = clock();
		iter = MAXITER;
		while (iter--)
		{
			a += buf[index];
			/* Ensure we stay within our buffer */
			index = (index + 1) & (BUFSIZE - 1);
		}
		end = clock();
		printf ("%.3f seconds.\n", ((double)(end - start))/CLOCKS_PER_SEC);


		printf ("Running data cache miss test... ");
		fflush (stdout);
		start = clock();
		iter = MAXITER;
		while (iter--)
		{
			a += buf[index];
			/* Ensure we stay within our buffer */
			index = (index + CACHE_LINE_SIZE) & (BUFSIZE - 1);
		}
		end = clock();
		cache_miss_time = ((double)(end - start))/CLOCKS_PER_SEC;
		printf ("%.3f seconds.\n", cache_miss_time);


		printf ("Running MMU miss test...        ");
		fflush (stdout);
		start = clock();
		iter = MAXITER;
		while (iter--)
		{
			a += buf[index];
			/* Ensure we stay within our buffer */
			index = (index + MMU_PAGE_SIZE) & (BUFSIZE - 1);
		}
		end = clock();
		mmu_miss_time = ((double)(end - start))/CLOCKS_PER_SEC;
		printf ("%.3f seconds.\n", mmu_miss_time);

		printf ("MMU overhead estimation: %.3f seconds (%.1f%%).\n",
			mmu_miss_time - cache_miss_time,
			((mmu_miss_time - cache_miss_time) * 100 / cache_miss_time) + 100);

		free (buf);
	}
	else
		printf ("Not enough memory.\n");

	return 0;
}
