Library Memory Standard, 2nd Draft

A proposal for high performance libraries


By Phil Frisbie, Jr.

Background

Many programmers use libraries to speed up the development time of applications. The standard C library is an example of a library used by most C applications. Other third party libraries provide enhanced functions for such things as graphics, sound, networking, and input. Many libraries also offer cross platform compatibility so that porting applications is easier.

Libraries vary in quality and functionality, but many have one flaw in common, and that is poor memory integration with the application. These libraries need to allocate temporary memory, and they simply use the C run-time (CRT) malloc and free functions. While this is fine for many applications, it can cause problems with high performance applications that use a custom memory manager (CMM).

High performance applications, such as games, can often receive a large boost in performance by using a CMM. The CRT memory functions are designed to be general purpose, but the CMM can be tuned to the type of memory allocations common in the application. The CMM can also include additional run-time debugging to help quickly track down memory access errors. When an application using a CMM is linked to a library that uses CRT memory functions, it will not perform it's best, it can make debugging harder, and it could cause memory leaks if memory is allocated by the CRT and the application tries to free it using the CMM.

There is another problem unique to Windows when the library is supplied as a DLL. There are two CRT DLLs (single and multithreaded) that the application or library might dynamically link to, or it may even be statically linked to one of those CRTs. So, there are 16 possible combinations of application and library run times! The following proposal also eliminates this problem.

The proposal

The Library Memory Standard is composed of four parts:

The API interface between the application and the library.
The internal library API.
The optional CMM for use inside the library (the library internal CMM, or LCMM).
Documentation listing the library functions that allocate and free memory.

The API interface provides a way to allow the library to be made aware of the application's CMM. The API is two functions with six type defines, as prototyped below. (Note: 'xyz' is the prefix used by the example library, and will be substituted by the library prefix in a real library)

/* These two function callbacks are required in the application */
typedef void *(*CMMmalloc)(size_t size, char *name);
typedef void (*CMMfree)(void *memblock, char *name);

/* This function callback is optional in the application */
typedef void *(*CMMrealloc)(void *memblock, size_t size, char *name);

/* These are required in the library interface */
void xyzSetCMM(CMMmalloc m, CMMfree f, CMMrealloc r);
void xyzHint(enum hint, int value);

The three function prototypes, CMMmalloc, CMMfree, and CMMrealloc are callback functions to be included in the application. The last parameter, a char string, may be used for debugging or memory profiling, or it may be ignored. A NULL char string must be accepted properly by these functions.

The first function, xyzSetCMM, is used to hook the library's memory allocation functions. This function, and xyzHint, should be called before any other library functions so that all memory allocations will use the application's CMM. If the application's CMM does not implement the equivalent to realloc, then pass NULL so that the library will use it's own realloc function.

The second function, xyzHint, is used to control the optional LCMM. If there is not an LCMM, then it will simply be a NOP. The hints will include at least the following: CMM_USE_LCMM, and CMM_CHUNK_SIZE. The CMM_USE_LCMM hint with a value of '0' will disable the use of the LCMM, and a value of '1' will enable it. The LCMM will be enabled by default, so if the LCMM is to be disabled xyzHint should be called before any other library calls, including xyzSetCMM. Enabling or disabling the LCMM is only guaranteed to succeed before other library functions are called, and the library may ignore additional calls to enable or disable the LCMM. The CMM_CHUNK_SIZE will hint at the preferred chunk size that the LCMM should use to allocate memory. The LCMM will then attempt to only allocate memory using the chunk size or a multiple of the chunk size. If the chunk size is not hinted, the default can be anything the LCMM prefers.

The internal library API consists of three functions:

void *LMSmalloc(size_t size, char *name);
void LMSfree(void *memblock, char *name);
void *LMSrealloc(void *memblock, size_t size, char *name);

These three functions are basically the ANSI C memory functions with an additional parameter. This additional parameter, a null terminated string no longer than 31 characters, can be used for debugging and/or memory profiling in either the CMM or LCMM. For example, if the CMM supports the debugging string, the following will help it track the allocation:

struct player_t *p;

p = (struct plater_t *)LMSmalloc(sizeof(struct player_t), "struct player_t");
....
LMSfree(p, "struct player_t");

Or the library could use this to record the source file name and line of the allocation:

#define mymalloc(pointer, size) {\
    char    filename__[256] = __FILE__; \
    char    temp__[300]; \
    sprintf(temp__, "%s, line %d", filename__, __LINE__); \
    pointer = LMSmalloc(size, temp__); \
}

mymalloc(p, sizeof(struct player_t));

If the library programmers do not want to use the char * argument, which might be the case for a closed source product, they may use NULL. However, to help future debugging, they would be advised to at least use an obfuscated name such as 'struct 112' or '5643', anything that will help them debug a memory allocation error if an application programmer finds one.

The optional LCMM will boost the performance of the library if the application does not have a CMM, but even if the application does have a CMM, it may not work well for the library. The LCMM will cause more memory to be allocated to the library, so performance testing should be done to see if the performance boost is worth the extra memory usage. The library code needed to implement the LCMM is beyond the scope of this proposal. If it is provided, it should be highly tuned to the needs of the library.

The last item, the documentation listing the library functions that allocate and free memory, is perhaps the easiest but most tedious part, and very important to the application programmer. This documentation should at least list which functions allocate and/or free memory. The approximate amount of memory allocated could also be useful in cases where the library will be using the application's CMM. The application's CMM could then be tuned for the common allocation sizes needed by the library.

Conclusion

These guidelines are designed to make libraries not only easier to use, but faster and better documented. They are not intentionally biased towards any operating system, compiler, or library type. Although my examples are specific to C applications and libraries, these guidelines can be adapted to any language that supports allocating and freeing memory dynamically.

Comments and suggestions are welcome, and this proposal will be updated accordingly. Send all comments to Phil Frisbie, Jr..


Page last modified: 10 August 2005
© 1998-2006 Hawk Software