Using GMemChunks



I'm profiling memory usage in GNOME, and noticed that a lot of places
allocate fixed-size blocks of memory. For example, gdk_window_new and
gdk_window_foreign_new allocate tons (257 in testgnome using a repeatable
test pattern) of GdkWindowPrivate structures directly, using g_malloc().

How efficient is GMemChunk, and how much of a gain would be given by using
it in even just this specific example? Is there a better way?

<ramblings to partially answer the question>
A LOT of places seem to use 8-byte memory chunks. Would it be reasonable
to make a generic "extern GMemChunk *eight_byte_chunk;" in glib for use by
any routines that see fit? (Half the problem is that GTree nodes are 24
bytes, and there's possibly a lot of overhead there just in storing the
list of free chunks :-) 

I've attached a program that I've been using to test, in case it's
helpful. It seems to indicate that GMemChunk is only useful for small
memchunks, but I'm not confident enough in the test program to be sure.
GMemChunk also seems slower in all cases.

Suggestions welcomed,
-- Elliot
Do you ever just feel thankful that you know me and have access to my
dementia? Explain. Be prepared to discuss in class.
/* Elliot's Wacky Memory Allocation Efficiency Tester */
#include <glib.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <stdio.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/wait.h>

#define NITERS 65535
#define NITEMS 4096

#ifdef PROFILING_VERSION
#define NRUNS 10
#endif

int start_memused;
void start_memsizing(void)
{
	struct mallinfo mi = mallinfo();

	start_memused = mi.uordblks;
}

int end_memsizing(int chunksize)
{
	struct mallinfo mi = mallinfo();
	return (mi.uordblks - start_memused) - (chunksize * NITEMS);
}

void
print_timediff(struct timeval *starttime, struct timeval *endtime)
{
	long long usecdiff;

	usecdiff = endtime->tv_sec * 1000000 /* cvt to microseconds */;
	usecdiff += endtime->tv_usec;

	usecdiff -= starttime->tv_sec * 1000000 /* cvt to microseconds */;
	usecdiff -= starttime->tv_usec;

	printf("%lld usecs\n", usecdiff);
}

int docheck_memchunk(int chunksize)
{
	gpointer array[NITEMS];
	int i, n;
	GMemChunk *mc;

#ifndef PROFILING_VERSION
	start_memsizing();
#endif

	mc = g_mem_chunk_new("Testme", chunksize, 1024, G_ALLOC_AND_FREE);

	for(i = 0; i < NITEMS; i++)
		array[i] = g_mem_chunk_alloc(mc);

	for(i = 0; i < NITERS; i++) {
		n = rand() % NITEMS;
		if(array[n]) {
			g_mem_chunk_free(mc, array[n]);
			array[n] = NULL;
		} else {
			array[n] = g_mem_chunk_alloc(mc);
		}
	}

	for(i = 0; i < NITEMS; i++)
		if(!array[i]) array[i] = g_mem_chunk_alloc(mc);

#ifdef PROFILING_VERSION
	for(i = 0; i < NITEMS; i++)
		g_mem_chunk_free(mc, array[i]);

	g_mem_chunk_destroy(mc);
#endif

#ifndef PROFILING_VERSION
	g_print("memchunk diff with chunksize %d is %d\n",
		chunksize, end_memsizing(chunksize));
#endif

	return 0;
}

int docheck_malloc(int chunksize)
{
	gpointer array[NITEMS];
	int i, n;

#ifndef PROFILING_VERSION
	start_memsizing();
#endif

	for(i = 0; i < NITEMS; i++)
		array[i] = g_malloc(chunksize);

	for(i = 0; i < NITERS; i++) {
		n = rand() % NITEMS;
		if(array[n]) {
			g_free(array[n]);
			array[n] = NULL;
		} else {
			array[n] = g_malloc(chunksize);
		}
	}
	for(i = 0; i < NITEMS; i++)
		if(!array[i]) array[i] = g_malloc(chunksize);

#ifdef PROFILING_VERSION
	for(i = 0; i < NITEMS; i++)
		g_free(array[i]);
#endif

#ifndef PROFILING_VERSION
	g_print("malloc diff with chunksize %d is %d\n",
		chunksize,
		end_memsizing(chunksize));
#endif

	return 0;
}

void docheck(int chunksize)
{
#ifdef PROFILING_VERSION
	int i;
	struct timeval starttime, endtime;
#else
	int pid, st;
#endif

#ifdef PROFILING_VERSION
	gettimeofday(&starttime, NULL);
	for(i = 0; i < NRUNS; i++) {
#endif
	/* We have to fork so we can get accurate alloc difference */
#ifdef PROFILING_VERSION
	docheck_memchunk(chunksize);
#else
	pid = fork();
	if(pid)
		waitpid(pid, &st, 0);
	else {
		docheck_memchunk(chunksize);
		fflush(stdout);
		_exit(0);
	}
#endif
#ifdef PROFILING_VERSION
	}
	gettimeofday(&endtime, NULL);
	printf("memchunk run on chunksize %d took ", chunksize);
	print_timediff(&starttime, &endtime);
	fflush(stdout);

	gettimeofday(&starttime, NULL);
	for(i = 0; i < NRUNS; i++) {
#endif
#ifdef PROFILING_VERSION
	docheck_malloc(chunksize);
#else
	pid = fork();
	if(pid)
		waitpid(pid, &st, 0);
	else {
		docheck_malloc(chunksize);
		fflush(stdout);
		_exit(0);
	}
#endif
#ifdef PROFILING_VERSION
	}
	gettimeofday(&endtime, NULL);
	printf("malloc run on chunksize %d took ", chunksize);
	print_timediff(&starttime, &endtime);

	printf("\n");
	fflush(stdout);
#endif
}

int main(int argc, char *argv[])
{
	int i;

	if(argc > 1)
		docheck(atoi(argv[1]));
	else
		for(i = 8; i < 256; i = (int)((double)i * 1.5))
			docheck(i);

	return 0;
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]