#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>

void *mymemcpy(void *dst, const void *src, size_t);
void *mymemcpy_asm(void *dst, const void *src, size_t);
void *mymemcpy_sse(void *dst, const void *src, size_t);
void *mymemcpy_beos(void *dst, const void *src, size_t);

#define BUFSIZE (16*1024*1024)
static char srcbuf[BUFSIZE + 128];
static char dstbuf[BUFSIZE + 128];

void *
slow_memcpy(void *to, const void *from, size_t size)
{
	char *tmp = (char *)to;
	char *s = (char *)from;

	while (size--)
		*tmp++ = *s++;

	return to;
}

void *
null_memcpy(void *to, const void *from, size_t size)
{
	return to;
}

long long timeroutine(void *(*func)(void *, const void *, size_t), void *to, const void *from, size_t len, int count)
{
	int i;
	struct timeval t1, t2;

	gettimeofday(&t1, 0);

	for (i=0; i < count; i++) {
		func(to, from, len);
	}

	gettimeofday(&t2, 0);

	return (t2.tv_sec * 1000000LL + t2.tv_usec) - (t1.tv_sec * 1000000LL + t1.tv_usec);
}

int main()
{
	long long nulltime;
	long long slowtime;
	long long ctime;
	long long asmtime;
	long long ssetime;
	long long beostime;

	const size_t bytes_copied = 16*1024*1024;
	const int iterations = bytes_copied / BUFSIZE;

	printf("0\n");
	mymemcpy_sse(dstbuf, srcbuf, 0);
	printf("1\n");
	mymemcpy_sse(dstbuf, srcbuf, 1);
	printf("15\n");
	mymemcpy_sse(dstbuf, srcbuf, 15);
	printf("aligned\n");
	mymemcpy_sse(dstbuf, srcbuf, 16);
	printf("unaligned\n");
	mymemcpy_sse(dstbuf+1, srcbuf, 16);
	printf("large\n");
	mymemcpy_sse(dstbuf, srcbuf, BUFSIZE);
	printf("done\n");

	// make sure the buffers are paged in
	memset(srcbuf, 0x99, sizeof(srcbuf));
	memset(dstbuf, 0x99, sizeof(dstbuf));

	// prime the sse
	mymemcpy_sse(dstbuf, srcbuf, BUFSIZE);

	int srcalign = 0;
	int dstalign = 0;

	for (srcalign = 0; srcalign <= 64; ) {
		for (dstalign = 0; dstalign <= 64; ) {
			nulltime = timeroutine(&null_memcpy, dstbuf + dstalign, srcbuf + srcalign, BUFSIZE, iterations);
			slowtime = timeroutine(&slow_memcpy, dstbuf + dstalign, srcbuf + srcalign, BUFSIZE, iterations);
			ctime = timeroutine(&mymemcpy, dstbuf + dstalign, srcbuf + srcalign, BUFSIZE, iterations);
			asmtime = timeroutine(&mymemcpy_asm, dstbuf + dstalign, srcbuf + srcalign, BUFSIZE, iterations);
			ssetime = timeroutine(&mymemcpy_sse, dstbuf + dstalign, srcbuf + srcalign, BUFSIZE, iterations);
			beostime = timeroutine(&mymemcpy_beos, dstbuf + dstalign, srcbuf + srcalign, BUFSIZE, iterations);

			slowtime -= nulltime;
			ctime -= nulltime;
			asmtime -= nulltime;
			
			printf("bufsize %d iterations %d srcalign %d dstalign %d\n", BUFSIZE, iterations, srcalign, dstalign);
			printf("slow time %lld, %lld bytes/sec\n", slowtime, bytes_copied * 1000000LL / slowtime);
			printf("C    time %lld, %lld bytes/sec\n", ctime, bytes_copied * 1000000LL / ctime);
			printf("asm  time %lld, %lld bytes/sec\n", asmtime, bytes_copied * 1000000LL / asmtime);
			printf("sse  time %lld, %lld bytes/sec\n", ssetime, bytes_copied * 1000000LL / ssetime);
			printf("beos time %lld, %lld bytes/sec\n", beostime, bytes_copied * 1000000LL / beostime);

			if (dstalign == 0)
				dstalign = 1;
			else if (dstalign < 4)
				dstalign++;
			else
				dstalign += 4;
		}
		if (srcalign == 0)
			srcalign = 1;
		else if (srcalign < 4)
			srcalign++;
		else
			srcalign += 4;
	}

	return 0;
}
