357 lines
7.8 KiB
C
357 lines
7.8 KiB
C
|
/* test.c
|
||
|
An example of how to use nedalloc
|
||
|
(C) 2005-2007 Niall Douglas
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include "nedmalloc.c"
|
||
|
|
||
|
#define THREADS 5
|
||
|
#define RECORDS (100000/THREADS)
|
||
|
#define TORTURETEST 1
|
||
|
|
||
|
static int whichmalloc;
|
||
|
static int doRealloc;
|
||
|
static struct threadstuff_t
|
||
|
{
|
||
|
int ops;
|
||
|
unsigned int *toalloc;
|
||
|
void **allocs;
|
||
|
char cachesync1[128];
|
||
|
int done;
|
||
|
char cachesync2[128];
|
||
|
} threadstuff[THREADS];
|
||
|
|
||
|
static void threadcode(int);
|
||
|
|
||
|
#ifdef WIN32
|
||
|
static DWORD WINAPI _threadcode(LPVOID a)
|
||
|
{
|
||
|
threadcode((int)(size_t) a);
|
||
|
return 0;
|
||
|
}
|
||
|
#define THREADVAR HANDLE
|
||
|
#define THREADINIT(v, id) (*v=CreateThread(NULL, 0, _threadcode, (LPVOID)(size_t) id, 0, NULL))
|
||
|
#define THREADSLEEP(v) SleepEx(v, FALSE)
|
||
|
#define THREADWAIT(v) (WaitForSingleObject(v, INFINITE), 0)
|
||
|
|
||
|
typedef unsigned __int64 usCount;
|
||
|
static FORCEINLINE usCount GetUsCount()
|
||
|
{
|
||
|
static LARGE_INTEGER ticksPerSec;
|
||
|
static double scalefactor;
|
||
|
LARGE_INTEGER val;
|
||
|
if(!scalefactor)
|
||
|
{
|
||
|
if(QueryPerformanceFrequency(&ticksPerSec))
|
||
|
scalefactor=ticksPerSec.QuadPart/1000000000000.0;
|
||
|
else
|
||
|
scalefactor=1;
|
||
|
}
|
||
|
if(!QueryPerformanceCounter(&val))
|
||
|
return (usCount) GetTickCount() * 1000000000;
|
||
|
return (usCount) (val.QuadPart/scalefactor);
|
||
|
}
|
||
|
|
||
|
static HANDLE win32heap;
|
||
|
static void *win32malloc(size_t size)
|
||
|
{
|
||
|
return HeapAlloc(win32heap, 0, size);
|
||
|
}
|
||
|
static void *win32realloc(void *p, size_t size)
|
||
|
{
|
||
|
return HeapReAlloc(win32heap, 0, p, size);
|
||
|
}
|
||
|
static void win32free(void *mem)
|
||
|
{
|
||
|
HeapFree(win32heap, 0, mem);
|
||
|
}
|
||
|
|
||
|
static void *(*const mallocs[])(size_t size)={ malloc, nedmalloc, win32malloc };
|
||
|
static void *(*const reallocs[])(void *p, size_t size)={ realloc, nedrealloc, win32realloc };
|
||
|
static void (*const frees[])(void *mem)={ free, nedfree, win32free };
|
||
|
#else
|
||
|
static void *_threadcode(void *a)
|
||
|
{
|
||
|
threadcode((int)(size_t) a);
|
||
|
return 0;
|
||
|
}
|
||
|
#define THREADVAR pthread_t
|
||
|
#define THREADINIT(v, id) pthread_create(v, NULL, _threadcode, (void *)(size_t) id)
|
||
|
#define THREADSLEEP(v) usleep(v*1000)
|
||
|
#define THREADWAIT(v) pthread_join(v, NULL)
|
||
|
|
||
|
typedef unsigned long long usCount;
|
||
|
static FORCEINLINE usCount GetUsCount()
|
||
|
{
|
||
|
struct timeval tv;
|
||
|
gettimeofday(&tv, 0);
|
||
|
return ((usCount) tv.tv_sec*1000000000000LL)+tv.tv_usec*1000000LL;
|
||
|
}
|
||
|
|
||
|
static void *(*const mallocs[])(size_t size)={ malloc, nedmalloc };
|
||
|
static void *(*const reallocs[])(void *p, size_t size)={ realloc, nedrealloc };
|
||
|
static void (*const frees[])(void *mem)={ free, nedfree };
|
||
|
#endif
|
||
|
static usCount times[THREADS];
|
||
|
|
||
|
|
||
|
static FORCEINLINE unsigned int myrandom(unsigned int *seed)
|
||
|
{
|
||
|
*seed=1664525UL*(*seed)+1013904223UL;
|
||
|
return *seed;
|
||
|
}
|
||
|
|
||
|
static void threadcode(int threadidx)
|
||
|
{
|
||
|
int n;
|
||
|
unsigned int *toallocptr=threadstuff[threadidx].toalloc;
|
||
|
void **allocptr=threadstuff[threadidx].allocs;
|
||
|
unsigned int seed=threadidx;
|
||
|
usCount start;
|
||
|
threadstuff[threadidx].done=0;
|
||
|
/*neddisablethreadcache(0);*/
|
||
|
THREADSLEEP(100);
|
||
|
start=GetUsCount();
|
||
|
#ifdef TORTURETEST
|
||
|
/* A randomised malloc/realloc/free test (torture test) */
|
||
|
for(n=0; n<RECORDS*100; n++)
|
||
|
{
|
||
|
unsigned int r=myrandom(&seed), i;
|
||
|
i=(int)(r % RECORDS);
|
||
|
if(!allocptr[i])
|
||
|
{
|
||
|
allocptr[i]=mallocs[whichmalloc](r & 0x1FFF);
|
||
|
threadstuff[threadidx].ops++;
|
||
|
}
|
||
|
else if(r & (1<<31))
|
||
|
{
|
||
|
allocptr[i]=reallocs[whichmalloc](allocptr[i], r & 0x1FFF);
|
||
|
threadstuff[threadidx].ops++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
frees[whichmalloc](allocptr[i]);
|
||
|
allocptr[i]=0;
|
||
|
}
|
||
|
}
|
||
|
for(n=0; n<RECORDS; n++)
|
||
|
{
|
||
|
if(allocptr[n])
|
||
|
{
|
||
|
frees[whichmalloc](allocptr[n]);
|
||
|
allocptr[n]=0;
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
/* A simple stack which allocates and deallocates off the top (speed test) */
|
||
|
for(n=0; n<RECORDS;)
|
||
|
{
|
||
|
#if 1
|
||
|
r=myrandom(&seed);
|
||
|
if(allocptr>threadstuff[threadidx].allocs && (r & 65535)<32760) /*<32760)*/
|
||
|
{ /* free */
|
||
|
--toallocptr;
|
||
|
--allocptr;
|
||
|
--n;
|
||
|
frees[whichmalloc](*allocptr);
|
||
|
*allocptr=0;
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
if(doRealloc && allocptr>threadstuff[threadidx].allocs && (r & 1))
|
||
|
{
|
||
|
allocptr[-1]=reallocs[whichmalloc](allocptr[-1], *toallocptr);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
allocptr[0]=mallocs[whichmalloc](*toallocptr);
|
||
|
allocptr++;
|
||
|
}
|
||
|
n++;
|
||
|
toallocptr++;
|
||
|
threadstuff[threadidx].ops++;
|
||
|
}
|
||
|
}
|
||
|
while(allocptr>threadstuff[threadidx].allocs)
|
||
|
{
|
||
|
frees[whichmalloc](*--allocptr);
|
||
|
}
|
||
|
#endif
|
||
|
times[threadidx]+=GetUsCount()-start;
|
||
|
neddisablethreadcache(0);
|
||
|
threadstuff[threadidx].done=1;
|
||
|
}
|
||
|
|
||
|
static double runtest()
|
||
|
{
|
||
|
unsigned int seed=1;
|
||
|
int n, i;
|
||
|
double opspersec=0;
|
||
|
THREADVAR threads[THREADS];
|
||
|
for(n=0; n<THREADS; n++)
|
||
|
{
|
||
|
unsigned int *toallocptr;
|
||
|
int m;
|
||
|
threadstuff[n].ops=0;
|
||
|
times[n]=0;
|
||
|
threadstuff[n].toalloc=toallocptr=calloc(RECORDS, sizeof(unsigned int));
|
||
|
threadstuff[n].allocs=calloc(RECORDS, sizeof(void *));
|
||
|
for(m=0; m<RECORDS; m++)
|
||
|
{
|
||
|
unsigned int size=myrandom(&seed);
|
||
|
if(size<(1<<30))
|
||
|
{ /* Make it two power multiple of less than 512 bytes to
|
||
|
model frequent C++ new's */
|
||
|
size=4<<(size & 7);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
size&=0x3FFF; /* < 16Kb */
|
||
|
/*size&=0x1FFF;*/ /* < 8Kb */
|
||
|
/*size=(1<<6)<<(size & 7);*/ /* < 8Kb */
|
||
|
}
|
||
|
*toallocptr++=size;
|
||
|
}
|
||
|
}
|
||
|
#ifdef TORTURETEST
|
||
|
for(n=0; n<THREADS; n++)
|
||
|
{
|
||
|
THREADINIT(&threads[n], n);
|
||
|
}
|
||
|
for(i=0; i<32; i++)
|
||
|
{
|
||
|
int found=-1;
|
||
|
do
|
||
|
{
|
||
|
for(n=0; n<THREADS; n++)
|
||
|
{
|
||
|
THREADSLEEP(100);
|
||
|
if(threadstuff[n].done)
|
||
|
{
|
||
|
found=n;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} while(found<0);
|
||
|
THREADWAIT(threads[found]);
|
||
|
threads[found]=0;
|
||
|
THREADINIT(&threads[found], found);
|
||
|
printf("Relaunched thread %d\n", found);
|
||
|
}
|
||
|
for(n=THREADS-1; n>=0; n--)
|
||
|
{
|
||
|
THREADWAIT(threads[n]);
|
||
|
threads[n]=0;
|
||
|
}
|
||
|
#else
|
||
|
#if 1
|
||
|
for(n=0; n<THREADS; n++)
|
||
|
{
|
||
|
THREADINIT(&threads[n], n);
|
||
|
}
|
||
|
for(n=THREADS-1; n>=0; n--)
|
||
|
{
|
||
|
THREADWAIT(threads[n]);
|
||
|
threads[n]=0;
|
||
|
}
|
||
|
#else
|
||
|
/* Quick realloc() test */
|
||
|
doRealloc=1;
|
||
|
for(n=0; n<THREADS; n++)
|
||
|
{
|
||
|
THREADINIT(&threads[n], n);
|
||
|
}
|
||
|
for(n=THREADS-1; n>=0; n--)
|
||
|
{
|
||
|
THREADWAIT(threads[n]);
|
||
|
threads[n]=0;
|
||
|
}
|
||
|
#endif
|
||
|
#endif
|
||
|
{
|
||
|
usCount totaltime=0;
|
||
|
int totalops=0;
|
||
|
for(n=0; n<THREADS; n++)
|
||
|
{
|
||
|
totaltime+=times[n];
|
||
|
totalops+=threadstuff[n].ops;
|
||
|
}
|
||
|
opspersec=1000000000000.0*totalops/totaltime*THREADS;
|
||
|
printf("This allocator achieves %lfops/sec under %d threads\n", opspersec, THREADS);
|
||
|
}
|
||
|
for(n=THREADS-1; n>=0; n--)
|
||
|
{
|
||
|
free(threadstuff[n].allocs); threadstuff[n].allocs=0;
|
||
|
free(threadstuff[n].toalloc); threadstuff[n].toalloc=0;
|
||
|
}
|
||
|
return opspersec;
|
||
|
}
|
||
|
|
||
|
int main(void)
|
||
|
{
|
||
|
double std=0, ned=0;
|
||
|
|
||
|
#if 0
|
||
|
{
|
||
|
usCount start, end;
|
||
|
start=GetUsCount();
|
||
|
THREADSLEEP(5000);
|
||
|
end=GetUsCount();
|
||
|
printf("Wait was %lf\n", (end-start)/1000000000000.0);
|
||
|
}
|
||
|
#endif
|
||
|
#ifdef WIN32
|
||
|
{ /* Force load of user32.dll so we can debug */
|
||
|
BOOL v;
|
||
|
SystemParametersInfo(SPI_GETBEEP, 0, &v, 0);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if(0)
|
||
|
{
|
||
|
printf("\nTesting standard allocator with %d threads ...\n", THREADS);
|
||
|
std=runtest();
|
||
|
}
|
||
|
if(1)
|
||
|
{
|
||
|
printf("\nTesting nedmalloc with %d threads ...\n", THREADS);
|
||
|
whichmalloc=1;
|
||
|
ned=runtest();
|
||
|
}
|
||
|
#ifdef WIN32
|
||
|
if(0)
|
||
|
{
|
||
|
ULONG data=2;
|
||
|
win32heap=HeapCreate(0, 0, 0);
|
||
|
HeapSetInformation(win32heap, HeapCompatibilityInformation, &data, sizeof(data));
|
||
|
HeapQueryInformation(win32heap, HeapCompatibilityInformation, &data, sizeof(data), NULL);
|
||
|
if(2!=data)
|
||
|
{
|
||
|
printf("The win32 low frag allocator won't work under a debugger!\n");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
printf("Testing win32 low frag allocator with %d threads ...\n\n", THREADS);
|
||
|
whichmalloc=2;
|
||
|
runtest();
|
||
|
}
|
||
|
HeapDestroy(win32heap);
|
||
|
}
|
||
|
#endif
|
||
|
if(std && ned)
|
||
|
{ // ned should have more ops/sec
|
||
|
printf("\n\nnedmalloc allocator is %lf times faster than standard\n", ned/std);
|
||
|
}
|
||
|
printf("\nPress a key to trim\n");
|
||
|
getchar();
|
||
|
nedmalloc_trim(0);
|
||
|
#ifdef _MSC_VER
|
||
|
printf("\nPress a key to end\n");
|
||
|
getchar();
|
||
|
#endif
|
||
|
return 0;
|
||
|
}
|