GMPAllocator

© 2005,2006 John Abbott
GNU Free Documentation License, Version 1.2



index page

User documentation for files GMPAllocator

The purpose of a [CoCoA::GMPAllocator] object is to enable a customized strategy of memory management for the GMP arbitrary precision library. It will give slightly improved run-time performance if your computation involves many big integers of about the same size. By default, the memory for numbers with up to about 40 decimal digits is handled specially. You may also indicate the maximum size of integer to be handled in this way.

To use CoCoA allocator for GMP values simply create an object of type [CoCoA::GMPAllocator] at the very start of your program; the object should be destroyed only at the end of your program, after all GMP values have been destroyed. Note: IT IS IMPERATIVE that you create the [CoCoA::GMPAllocator] object before creating the [GlobalManager]. Furthermore, no GMP values may be created (or even destroyed) after the [GMPAllocator] object has been destroyed. Failure to observe these conditions will likely produce a crash (or worse).

By default a [GMPAllocator] handles specially values fitting into 16 bytes. You may specify a different size limit (in bytes) when you create the [GMPAllocator], but when choosing the size limit be aware that each GMP value below the specified size limit will occupy exactly the number of bytes specified even if the number could fit into a smaller space. So if you specify a large limit, you may waste lots of space if there are many values much smaller than the limit.

You are advised to see the example program in [examples/ex-GMPAllocator.C] If you have any doubts whatsoever about using a [GMPAllocator], don't! You should not create more than one [GMPAllocator] object.

Here are two examples uses in outline:

(1) Example of Default use of GMPAllocator.

    {
      GMPAllocator GMPMemMgr; // special mem mgt for GMPs fitting into 16 bytes
      ...
      // computations with GMP values
      ...
    }

(2) Example of use of GMPAllocator with user specified size limit.

    {
      GMPAllocator GMPMemMgr(100); // special mem mgt for GMPs up to 100 bytes
      ...
      // computations with GMP values
      ...
    }

Maintainer documentation for files GMPAllocator

The design of the implementation was to satisfy two needs: - (1) to allow faster GMP alloc/free for small GMP values; - (2) to permit monitoring of memory usage by GMP.

Aspect (1) is addressed by using a [MemPool] to handle small memory requests: any request which would fit into a single slice leads to allocation of a whole slice (possibly wasting some space). The size of a slice defines the meaning of "small GMP value"; it is specified by the argument to the [GMPAllocator] ctor which defaults to 16 (bytes). See also a note in "Bugs, Shortcomings, etc".

Aspect (2) is partly addressed by the MemPool since a MemPool offers monitoring facilities (when CoCoA_MEMPOOL_DEBUG is positive).

The implementation is fairly straightforward. The basic idea is to use a [MemPool] to store all small GMP values, and to let the system allocator handle space for larger values. Fortunately, the GMP library supplies the block size even when deallocating (and the old block size when reallocating), so it is easy to tell who had allocated the block.

Here is the reasoning behind the global variable [GMPPoolPtr]. I have used a file-local global variable to indicate which [MemPool] to use because this is only the way to pass the information to the alloc/free/realloc functions. I have chosen to make the actual [MemPool] object a data member of [GMPAllocator] so that I can control when its destructor is called; this is important when gathering statistics in "debug mode". Making the [MemPool] itself a global object would mean its destructor is called only after [main] has exited -- with no guarantees about what would happen to the debugging output. Since the [MemPool] object belongs to some [GMPAllocator] object, the global variable should be a non-owning pointer with the convention that the pointer value be [NULL] when it points to no object.

Of the alloc/free/realloc functions which are handed to GMP, only [CoCoA_GMP_realloc] displays any complication. GMP limbs can be stored either in memory supplied by the [MemPool] belonging to a [GMPAllocator] object or in system allocated memory; a reallocation could cause the limbs to be moved from one sort of memory to the other.

Bugs, Shortcomings and other ideas

What is the best slice size to use? Too large a value may lead to wastage of memory space, while too small a value will result in too many requests being passed to the system allocator. I suspect that a value between 8 and 16 (bytes) is probably a good choice on many platforms. Now the (advanced) user can specify the slice size to use when creating the [GMPAllocator] object. Perhaps the size should be rounded up to the next multiple of 4 (or even of 8).

The conceptual design is rather too fragile, but I do not see a robuster way. Part of the fragility seems to reside in the interface to GMP.

I do not like the use of a global variable (viz. [GMPPoolPtr]), but it seems inevitable. I'd rather not consider what would happen in a multithreaded execution with potential race conditions, etc.

All code for debugging and gathering statistics is missing (except that which is already included in [MemPool]).

The destructor for GMPAllocator could reset the GMP allocator functions to the system ones; this might be useful for advanced users. Currently, the code will follow a null pointer if someone attempts to allocate space for a small GMP value after the destructor for GMPAllocator has been called; this is probably desirable behaviour for apprentices.

Is it possible to throw a std::bad_alloc exception when GMP asks for too much memory? How would this interact with the GMP library?