Overview

The heaps in ServerKit are very simple thread-safe fixed-size allocators with builtin reference counting.

The purpose of these heaps is to provide a fast heap allocator that keeps the memory around even after being freed (garbage collection / heap shrinking is currently supported as an option but ignored in operation, shrinking will probably be added soon).

When you create a new heap you define the size of the individual units to be allocated on the heap, how many units for the heap code to allocate via libc malloc() at a time, the maximum units to allow the heap growth to reach, and some options tweaking the behavior.

Some common uses for these style heaps are:

An important limitation of these heaps is their fixed unit size, this is one major specialization that makes them efficient, allocations do not involve any complex contiguous memory searches of specific size - except when the heap has to grow which invokes malloc().

In multi-threaded programming it is also important to have local heaps to specific allocation domains. If your application simply uses malloc() and free() everywhere for all memory needs, the process-wide synchronization required in libc to make malloc() and free() thread-safe will serialize all the threads concurrently using these functions, hurting parallelization.

An example of this would be say, an application with many threads that consume messages from various sources via queues. You would probably want to have a heap per queue (if you used a ServerKit queue primitive it comes with a local heap builtin). This allows a thread to allocate a queue entry destined for one thread while another thread does the same on a different threads queue (if SMP is available). If they were all using malloc/free directly the allocations and frees would get serialized. When you have provided local heaps for the local allocation domains the serialization is a worse case situation that only happens when the producers collide on the same consumer, but there is potential for parallel execution, it's not forced to be the rule.

A large part of writing good multi-threaded programs is carrying knowledge of locality, domains, and contexts, as far down to the metal as is practical. The moment you give up control in your code and hand it off to some global library or system call, it's very likely awareness of your programs needs has degraded and something is being done sub-optimally due to being a generic interface. POSIX threads gives us an interface to the kernel to convey some of these details through, and it's reasonably effective at this. However you will probably find libc and other middleware are frequently not so cooperative. Note that ServerKit is not an exception to this statement, while effort has been made to keep it relatively thin and tight, no doubt ServerKit has to make compromises to achieve it's goals as well.

2007-12-06