Sunday 13 November 2011

Why does my compiler provide two versions of malloc()? in C programming

Why does my compiler provide two versions of malloc()?

By including stdlib.h, you can use malloc() and free() in your code. This function is put in your code by
the compiler from the standard C library. Some compilers have a separate library that you can ask thecompiler to use (by specifying a flag such as -lmalloc on the command line) to replace the standard library’sversions of malloc() and free() with a different version.

These alternative versions of malloc() and free() do the same thing as the standard ones, but they are supposedly implemented to provide better performance at the cost of being less forgiving about memory allocation errors. I have never had a reason to use these alternative routines in 15 years of C programming.But in answering this FAQ, I wrote a simple test program to heavily exercise malloc() and free() and compiled it with a well-known commercial C compiler both with and without the malloc library. I couldn’t detect any significant difference in performance, and because both versions of the routines were the same size,I suspect that this particular vendor used the same code for both implementations. For this reason, I will notname the compiler vendor.

The moral of the story is that you probably don’t need to bother with the other version of malloc() and probably shouldn’t count on it for performance improvements. If profiling shows that your program spends a large percentage of its time in malloc() and free(), and you can’t fix the problem by changing the algorithm, you might be able to improve performance by writing your own “pool” allocator.

Programs that call malloc() and free() a lot are often allocating and freeing the same type of data, which
has a fixed size. When you know the size of the data to be allocated and freed, a pool allocator can be much faster than malloc() and free(). A pool allocator works by calling malloc() to allocate many structures of the same size all at once, then hands them out one at a time. It typically never calls free(), and the memory stays reserved for use by the pool allocator until the program exits. Listing XII.12 shows a pool allocator for the hypothetical type struct foo.

An example of a pool allocator.

#include <stdio.h>
/* declaration of hypothetical structure “foo” */
struct foo {
int dummy1;
char dummy2;
long dummy3;
};
/* start of code for foo pool allocator */
#include <stdlib.h>
/* number of foos to malloc() at a time */
#define NFOOS 64
/*
* A union is used to provide a linked list that
* can be overlaid on unused foos.
*/
union foo_u {
union foo_u *next;
struct foo f;
};
static union foo_u *free_list;
struct foo *
alloc_foo()
{
struct foo *ret = 0;
if (!free_list) {
int i;
free_list = (union foo_u *) malloc(NFOOS
* sizeof(union foo_u));
if (free_list) {
for (i = 0; i < NFOOS - 1; i++)
free_list[i].next =
&free_list[i + 1];
free_list[NFOOS - 1].next = NULL;
}
}
if (free_list) {
ret = &free_list->f;
free_list = free_list->next;
}
return ret;
}
void
free_foo(struct foo *fp)
{
union foo_u *up = (union foo_u *) fp;
up->next = free_list;
free_list = up;
}
int
main(int argc, char **argv)
{
int i;
int n;
struct foo **a;
if (argc < 2) {
fprintf(stderr, “usage: %s f\n”, argv[0]);
fprintf(stderr, “where f is the number of”);
fprintf(stderr, “ ‘foo’s to allocate\n”);
exit(1);
}
i = atoi(argv[1]);
a = (struct foo **) malloc(sizeof(struct foo *) * i);
for (n = 0; n < i; n++)
a[n] = alloc_foo();
for (n = 0; n < i; n++)
free_foo(a[n]);
return 0;
}

I compiled and ran this program with an argument of 300000 and compared the results to a similar program that replaced calls to alloc_foo() and free_foo() with calls to malloc() and free(). The CPU time used by the version of the program using the pool allocator was 0.46 seconds. The version of the program that used malloc() took 0.92 seconds.

Note that you should use a pool allocator only as a last resort. It might improve speed, but it can be very wasteful of memory. It also can lead to subtle memory allocation errors if you’re not careful to return memory to the pool from which it came instead of calling free().

Cross Reference:

VII.21: What is the heap?
VII.26: How does free() know how much memory to release?

No comments:

Post a Comment