Memory management
Table of Contents
1. Memory data structure for the context
The QMCkl library maintains detailed information about all memory allocations made through the context. This tracking enables automatic cleanup when the context is destroyed and provides visibility into memory usage patterns.
Every time a new block of memory is allocated, the information
relative to the allocation is stored in a new qmckl_memory_info_struct.
A qmckl_memory_info_struct contains the pointer to the memory block,
its size in bytes, and extra implementation-specific information such as
alignment requirements, memory pinning status, whether the memory should be
allocated on CPU or GPU, and other hardware-specific attributes.
This metadata structure can be extended in specialized implementations to support additional allocation strategies or memory types without changing the public API.
typedef struct qmckl_memory_info_struct { size_t size; void* pointer; } qmckl_memory_info_struct; static const qmckl_memory_info_struct qmckl_memory_info_struct_zero = { .size = (size_t) 0, .pointer = NULL };
The memory element of the context is a data structure which
contains an array of qmckl_memory_info_struct, the size of the
array, and the number of allocated blocks. This dynamic array grows
as needed to accommodate new allocations, providing a complete registry
of all memory managed by the context.
2. Passing info to allocation routines
Allocation parameters should be passed to the allocation routine using
an instance of a qmckl_memory_info_struct. This structure-based approach
allows for extensibility - new allocation parameters can be added to the
structure in future versions without changing function signatures. It also
makes the allocation requests more self-documenting, as the qmckl_memory_info_struct
explicitly specifies all allocation requirements.
3. Allocation/deallocation functions
3.1. Memory allocation
Memory allocation inside the library should be done with
qmckl_malloc. This function provides a centralized allocation mechanism
that allows the library to choose how and where memory will be allocated.
A pointer is returned to the caller, similar to standard malloc.
The context is passed to the function so that the library can store data related to the allocation inside the context. In this particular implementation of the library, we maintain a registry of allocated pointers so that all memory can be properly freed when the library is de-initialized. This prevents memory leaks and ensures clean shutdown.
If the allocation fails, the NULL pointer is returned, following standard
C conventions. Callers should always check for NULL returns and handle
allocation failures appropriately.
For security and deterministic behavior, the allocated memory block is
zeroed using memset before being returned to the caller.
void* qmckl_malloc(qmckl_context context, const qmckl_memory_info_struct info);
Here's a step-by-step explanation of qmckl_malloc:
- The function takes two parameters: a
qmckl_contextand aqmckl_memory_info_structcontaining the desired size of the memory block to allocate. - The function checks if the provided
qmckl_contextis valid, using theqmckl_context_checkfunction. - The
qmckl_context_structpointer is retrieved from the providedqmckl_context. - The function then allocates memory:
If the
HAVE_HPCandHAVE_POSIX_MEMALIGNmacros are defined, the memory allocation is done using thealigned_allocfunction with a 64-byte alignment, rounding up the requested size to the nearest multiple of 64 bytes. Else, the memory allocation is done using the standardmallocfunction.
5 If the allocation fails, the function returns NULL.
- The allocated memory block is zeroed using
memset. - The function acquires a lock on the
qmckl_contextusingqmckl_lock. - Inside the locked section, the function checks if the
qmckl_memory_structis full. If it is, it reallocates a larger array by doubling its size and updating thearray_sizemember of theqmckl_memory_struct. - The function finds the first available
qmckl_memory_info_structslot in the element array of theqmckl_memory_struct.
When freeing the memory with qmckl_free, the context is passed, in
case some important information has been stored related to memory
allocation and needs to be updated.
qmckl_exit_code qmckl_free(qmckl_context context, void * const ptr);
Here's a step-by-step explanation of the qmckl_free function:
- The function takes two parameters: a
qmckl_contextand a pointer to the memory block (ptr) that needs to be deallocated. - The function checks if the provided
qmckl_contextis valid, using theqmckl_context_checkfunction. If it is not valid, it returns an error codeQMCKL_INVALID_CONTEXTusing theqmckl_failwithfunction. - The function checks if the provided pointer is
NULL. If it is, it returns an error codeQMCKL_INVALID_ARG_2using theqmckl_failwithfunction. - The
qmckl_context_structpointer is retrieved from the providedqmckl_context. - The function acquires a lock on the
qmckl_contextusingqmckl_lock. - Inside the locked section, the function searches for the pointer in
the element array of the
qmckl_memory_struct. - If the pointer is not found in the array, it releases the lock and
returns an error code
QMCKL_INVALID_ARG_2using theqmckl_failwithfunction. - If the pointer is found, the memory block is deallocated using the
standard
freefunction. - The
qmckl_memory_info_structat the found position is zeroed usingmemset. This marks the slot as available for future allocations.
- The
n_allocatedmember of theqmckl_memory_structis decremented by one, as the memory block has been deallocated. - The function releases the lock on the
qmckl_contextusingqmckl_unlock. - Finally, the function returns
QMCKL_SUCCESSto indicate successful deallocation of the memory block.
4. Get the size of a memory block
All the blocks allocated with qmckl_malloc keep track of how many
bytes were allocated. Using qmckl_malloc_size allows to get this information.
qmckl_exit_code qmckl_get_malloc_info(qmckl_context context, const void* pointer, qmckl_memory_info_struct* info);