1
0
mirror of https://github.com/TREX-CoE/qmckl.git synced 2024-06-01 02:45:43 +02:00
qmckl/org/qmckl_memory.org

473 lines
14 KiB
Org Mode
Raw Permalink Normal View History

2021-03-09 01:16:23 +01:00
#+TITLE: Memory management
2021-04-30 01:26:19 +02:00
#+SETUPFILE: ../tools/theme.setup
#+INCLUDE: ../tools/lib.org
2020-10-22 01:24:14 +02:00
2021-03-09 01:16:23 +01:00
We override the allocation functions to enable the possibility of
optimized libraries to fine-tune the memory allocation.
2020-10-16 23:56:22 +02:00
2021-03-09 01:16:23 +01:00
* Headers :noexport:
#+begin_src c :tangle (eval c)
2021-05-10 10:05:50 +02:00
#ifdef HAVE_CONFIG_H
2021-05-10 10:41:59 +02:00
#include "config.h"
2021-05-09 02:12:38 +02:00
#endif
2021-05-10 10:05:50 +02:00
#ifdef HAVE_STDINT_H
2021-03-09 01:16:23 +01:00
#include <stdint.h>
2021-05-10 10:05:50 +02:00
#elif HAVE_INTTYPES_H
#include <inttypes.h>
#endif
2021-03-09 01:16:23 +01:00
#include <stdlib.h>
2021-03-30 22:40:56 +02:00
#include <string.h>
2021-03-10 12:58:38 +01:00
#include <assert.h>
2021-05-11 13:57:23 +02:00
#include "qmckl.h"
2021-03-30 22:40:56 +02:00
#include "qmckl_memory_private_type.h"
2021-03-30 14:51:23 +02:00
#include "qmckl_context_private_type.h"
2021-03-30 22:40:56 +02:00
#include "qmckl_memory_private_func.h"
2021-03-09 01:16:23 +01:00
#+end_src
#+begin_src c :tangle (eval c_test) :noweb yes
2020-10-17 01:10:54 +02:00
#include "qmckl.h"
2021-05-11 16:41:03 +02:00
#include "assert.h"
2021-05-10 10:05:50 +02:00
#ifdef HAVE_CONFIG_H
2021-05-10 10:41:59 +02:00
#include "config.h"
2021-05-09 02:12:38 +02:00
#endif
2021-03-30 22:40:56 +02:00
#include "qmckl_context_private_type.h"
#include "qmckl_memory_private_func.h"
2021-05-11 16:41:03 +02:00
int main() {
2021-03-09 01:16:23 +01:00
#+end_src
2020-10-16 23:56:22 +02:00
2021-03-30 22:40:56 +02:00
2021-10-14 21:40:14 +02:00
#+begin_src c :tangle (eval h_private_func) :noweb yes
#ifndef QMCKL_MEMORY_HPF
#define QMCKL_MEMORY_HPF
#+end_src
2021-03-30 22:40:56 +02:00
#+begin_src c :tangle (eval h_private_type) :noweb yes
#ifndef QMCKL_MEMORY_HPT
#define QMCKL_MEMORY_HPT
#include <stdint.h>
2021-05-12 23:51:59 +02:00
#include <stdlib.h>
2021-03-30 22:40:56 +02:00
#+end_src
* Memory data structure for the context
2021-04-30 01:26:19 +02:00
2021-03-30 22:40:56 +02:00
Every time a new block of memory is allocated, the information
relative to the allocation is stored in a new ~qmckl_memory_info_struct~.
2021-04-30 01:26:19 +02:00
A ~qmckl_memory_info_struct~ contains the pointer to the memory block,
2021-03-30 22:40:56 +02:00
its size in bytes, and extra implementation-specific information such as
alignment, pinning, if the memory should be allocated on CPU or GPU
/etc/.
#+begin_src c :tangle (eval h_private_type) :noweb yes
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
};
#+end_src
2021-04-30 01:26:19 +02:00
2021-03-30 22:40:56 +02:00
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.
2021-04-30 01:26:19 +02:00
2021-03-30 22:40:56 +02:00
#+begin_src c :tangle (eval h_private_type) :noweb yes
typedef struct qmckl_memory_struct {
size_t n_allocated;
size_t array_size;
qmckl_memory_info_struct* element;
} qmckl_memory_struct;
#+end_src
* Passing info to allocation routines
Passing information to the allocation routine should be done by
passing an instance of a ~qmckl_memory_info_struct~.
2021-04-30 01:26:19 +02:00
2021-03-30 22:40:56 +02:00
* Allocation/deallocation functions
2021-03-18 23:55:50 +01:00
2021-03-10 12:58:38 +01:00
Memory allocation inside the library should be done with
~qmckl_malloc~. It lets the library choose how the memory will be
allocated, and a pointer is returned to the user. The context is
passed to let the library store data related to the allocation
inside the context. In this particular implementation of the library,
we store a list of allocated pointers so that all the memory can be
properly freed when the library is de-initialized.
If the allocation failed, the ~NULL~ pointer is returned.
2023-03-31 19:20:25 +02:00
The allocated memory block is zeroed using ~memset~.
2021-03-10 12:58:38 +01:00
# Header
2021-03-30 22:40:56 +02:00
#+begin_src c :tangle (eval h_private_func) :noexport
2021-03-10 12:58:38 +01:00
void* qmckl_malloc(qmckl_context context,
2021-03-30 22:40:56 +02:00
const qmckl_memory_info_struct info);
2021-03-09 01:16:23 +01:00
#+end_src
2021-04-30 01:26:19 +02:00
2023-03-31 19:20:25 +02:00
Here's a step-by-step explanation of ~qmckl_malloc~:
1. The function takes two parameters: a ~qmckl_context~ and a
~qmckl_memory_info_struct~ containing the desired size of the memory
block to allocate.
2. The function checks if the provided ~qmckl_context~ is valid, using the
~qmckl_context_check~ function.
3. The ~qmckl_context_struct~ pointer is retrieved from the provided
~qmckl_context~.
4. The function then allocates memory:
2023-10-06 11:33:33 +02:00
If the ~HAVE_HPC~ and ~HAVE_POSIX_MEMALIGN~ macros are defined, the memory
2023-09-14 09:53:17 +02:00
allocation is done using the ~aligned_alloc~ function 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 standard ~malloc~ function.
2023-03-31 19:20:25 +02:00
5 If the allocation fails, the function returns ~NULL~.
6. The allocated memory block is zeroed using ~memset~.
7. The function acquires a lock on the ~qmckl_context~ using ~qmckl_lock~.
8. Inside the locked section, the function checks if the
~qmckl_memory_struct~ is full. If it is, it reallocates a larger array
by doubling its size and updating the ~array_size~ member of the
~qmckl_memory_struct~.
9. The function finds the first available ~qmckl_memory_info_struct~ slot
in the element array of the ~qmckl_memory_struct~.
2021-03-10 12:58:38 +01:00
# Source
#+begin_src c :tangle (eval c)
2021-03-30 22:40:56 +02:00
void* qmckl_malloc(qmckl_context context, const qmckl_memory_info_struct info) {
2020-10-16 23:56:22 +02:00
2021-03-30 14:51:23 +02:00
assert (qmckl_context_check(context) != QMCKL_NULL_CONTEXT);
2021-03-10 12:58:38 +01:00
2022-04-05 11:03:38 +02:00
qmckl_context_struct* const ctx = (qmckl_context_struct*) context;
2021-03-30 22:40:56 +02:00
/* Allocate memory and zero it */
2023-10-06 11:33:33 +02:00
void * pointer = NULL;
#if defined(HAVE_HPC) && defined(HAVE_POSIX_MEMALIGN)
if (posix_memalign(&pointer, 64, info.size) != 0) pointer = NULL;
2022-06-29 13:59:09 +02:00
#else
2023-10-06 11:33:33 +02:00
pointer = malloc(info.size);
2022-06-29 13:59:09 +02:00
#endif
2021-03-30 22:40:56 +02:00
if (pointer == NULL) {
return NULL;
2021-03-10 12:58:38 +01:00
}
2021-03-30 22:40:56 +02:00
memset(pointer, 0, info.size);
qmckl_lock(context);
{
/* If qmckl_memory_struct is full, reallocate a larger one */
if (ctx->memory.n_allocated == ctx->memory.array_size) {
const size_t old_size = ctx->memory.array_size;
2021-05-12 23:57:40 +02:00
qmckl_memory_info_struct * new_array = realloc(ctx->memory.element,
2L * old_size *
2021-03-30 22:40:56 +02:00
sizeof(qmckl_memory_info_struct));
if (new_array == NULL) {
qmckl_unlock(context);
2021-03-31 01:52:43 +02:00
free(pointer);
2021-03-30 22:40:56 +02:00
return NULL;
}
2021-04-30 01:26:19 +02:00
2021-03-30 22:40:56 +02:00
memset( &(new_array[old_size]), 0, old_size * sizeof(qmckl_memory_info_struct) );
ctx->memory.element = new_array;
ctx->memory.array_size = 2L * old_size;
}
2021-04-30 01:26:19 +02:00
2021-03-30 22:40:56 +02:00
/* Find first NULL entry */
size_t pos = (size_t) 0;
while ( pos < ctx->memory.array_size && ctx->memory.element[pos].size > (size_t) 0) {
pos += (size_t) 1;
}
assert (ctx->memory.element[pos].size == (size_t) 0);
2021-03-10 12:58:38 +01:00
2021-03-30 22:40:56 +02:00
/* Copy info at the new location */
2022-08-07 14:57:10 +02:00
memcpy(&(ctx->memory.element[pos]), &info, sizeof(qmckl_memory_info_struct));
2021-03-30 22:40:56 +02:00
ctx->memory.element[pos].pointer = pointer;
ctx->memory.n_allocated += (size_t) 1;
2023-03-31 19:20:25 +02:00
//printf("MALLOC: %5ld %p\n", ctx->memory.n_allocated, ctx->memory.element[pos].pointer);
2021-03-30 22:40:56 +02:00
}
qmckl_unlock(context);
2021-04-30 01:26:19 +02:00
2021-03-10 12:58:38 +01:00
return pointer;
}
#+end_src
2021-03-18 23:55:50 +01:00
2021-03-09 01:16:23 +01:00
2021-03-18 23:55:50 +01:00
# Test :noexport:
2021-03-09 01:16:23 +01:00
#+begin_src c :tangle (eval c_test)
2021-03-30 22:40:56 +02:00
/* Create a context */
2021-04-30 01:26:19 +02:00
qmckl_context context = qmckl_context_create();
2021-03-09 01:16:23 +01:00
2021-03-30 22:40:56 +02:00
qmckl_memory_info_struct info = qmckl_memory_info_struct_zero;
2022-07-11 09:46:13 +02:00
info.size = (size_t) 3*sizeof(int);
2021-03-18 23:55:50 +01:00
2021-03-30 22:40:56 +02:00
/* Allocate an array of ints */
int *a = (int*) qmckl_malloc(context, info);
/* Check that array of ints is OK */
2021-05-11 16:41:03 +02:00
assert(a != NULL);
a[0] = 1; assert(a[0] == 1);
a[1] = 2; assert(a[1] == 2);
a[2] = 3; assert(a[2] == 3);
2021-03-30 22:40:56 +02:00
/* Allocate another array of ints */
int *b = (int*) qmckl_malloc(context, info);
/* Check that array of ints is OK */
2021-05-11 16:41:03 +02:00
assert(b != NULL);
b[0] = 1; assert(b[0] == 1);
b[1] = 2; assert(b[1] == 2);
b[2] = 3; assert(b[2] == 3);
2021-03-18 23:55:50 +01:00
#+end_src
2021-03-09 01:16:23 +01:00
2021-03-18 23:55:50 +01:00
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.
2021-03-09 01:16:23 +01:00
2022-01-06 18:54:31 +01:00
#+begin_src c :tangle (eval h_private_func)
2021-03-09 01:16:23 +01:00
qmckl_exit_code qmckl_free(qmckl_context context,
2021-03-30 22:40:56 +02:00
void * const ptr);
2021-03-09 01:16:23 +01:00
#+end_src
2021-03-05 03:45:30 +01:00
2023-03-31 19:20:25 +02:00
Here's a step-by-step explanation of the ~qmckl_free~ function:
1. The function takes two parameters: a ~qmckl_context~ and a pointer to
the memory block (~ptr~) that needs to be deallocated.
2. The function checks if the provided ~qmckl_context~ is valid, using the
~qmckl_context_check~ function. If it is not valid, it returns an error
code ~QMCKL_INVALID_CONTEXT~ using the ~qmckl_failwith~ function.
3. The function checks if the provided pointer is ~NULL~. If it is, it
returns an error code ~QMCKL_INVALID_ARG_2~ using the ~qmckl_failwith~
function.
4. The ~qmckl_context_struct~ pointer is retrieved from the provided
~qmckl_context~.
5. The function acquires a lock on the ~qmckl_context~ using ~qmckl_lock~.
6. Inside the locked section, the function searches for the pointer in
the element array of the ~qmckl_memory_struct~.
7. If the pointer is not found in the array, it releases the lock and
returns an error code ~QMCKL_INVALID_ARG_2~ using the ~qmckl_failwith~
function.
8. If the pointer is found, the memory block is deallocated using the
standard ~free~ function.
9. The ~qmckl_memory_info_struct~ at the found position is zeroed
using ~memset~. This marks the slot as available for future
allocations.
10. The ~n_allocated~ member of the ~qmckl_memory_struct~ is decremented
by one, as the memory block has been deallocated.
11. The function releases the lock on the ~qmckl_context~ using ~qmckl_unlock~.
12. Finally, the function returns ~QMCKL_SUCCESS~ to indicate
successful deallocation of the memory block.
2021-03-18 23:55:50 +01:00
# Source
2021-03-09 01:16:23 +01:00
#+begin_src c :tangle (eval c)
2021-03-30 22:40:56 +02:00
qmckl_exit_code qmckl_free(qmckl_context context, void * const ptr) {
2021-03-10 12:58:38 +01:00
2021-03-30 22:40:56 +02:00
if (qmckl_context_check(context) == QMCKL_NULL_CONTEXT) {
2021-03-10 12:58:38 +01:00
return qmckl_failwith(context,
2021-03-30 22:40:56 +02:00
QMCKL_INVALID_CONTEXT,
2021-03-10 12:58:38 +01:00
"qmckl_free",
2021-03-30 22:40:56 +02:00
NULL);
}
2021-04-30 01:26:19 +02:00
2021-03-30 22:40:56 +02:00
if (ptr == NULL) {
return qmckl_failwith(context,
QMCKL_INVALID_ARG_2,
"qmckl_free",
"NULL pointer");
}
2021-04-30 01:26:19 +02:00
2022-04-05 11:03:38 +02:00
qmckl_context_struct* const ctx = (qmckl_context_struct*) context;
2021-03-30 22:40:56 +02:00
qmckl_lock(context);
{
/* Find pointer in array of saved pointers */
size_t pos = (size_t) 0;
while ( pos < ctx->memory.array_size && ctx->memory.element[pos].pointer != ptr) {
pos += (size_t) 1;
2021-03-10 12:58:38 +01:00
}
2021-04-30 01:26:19 +02:00
2021-03-30 22:40:56 +02:00
if (pos >= ctx->memory.array_size) {
/* Not found */
qmckl_unlock(context);
return qmckl_failwith(context,
2022-08-07 14:57:10 +02:00
QMCKL_INVALID_ARG_2,
2021-03-30 22:40:56 +02:00
"qmckl_free",
"Pointer not found in context");
}
2023-03-31 19:20:25 +02:00
/* Found */
2021-03-30 22:40:56 +02:00
free(ptr);
2021-03-10 12:58:38 +01:00
2021-03-30 22:40:56 +02:00
ctx->memory.n_allocated -= (size_t) 1;
2023-03-31 19:20:25 +02:00
//printf("FREE : %5ld %p\n", ctx->memory.n_allocated, ctx->memory.element[pos].pointer);
ctx->memory.element[pos] = qmckl_memory_info_struct_zero;
2021-03-10 12:58:38 +01:00
}
2021-03-30 22:40:56 +02:00
qmckl_unlock(context);
2021-03-05 03:45:30 +01:00
return QMCKL_SUCCESS;
2020-10-16 23:56:22 +02:00
}
2021-03-09 01:16:23 +01:00
#+end_src
2020-10-22 01:24:14 +02:00
2021-03-18 23:55:50 +01:00
# Test
#+begin_src c :tangle (eval c_test) :exports none
2021-03-05 03:45:30 +01:00
qmckl_exit_code rc;
2021-03-30 22:40:56 +02:00
/* Assert that both arrays are allocated */
2021-05-11 16:41:03 +02:00
assert(a != NULL);
assert(b != NULL);
2021-03-30 22:40:56 +02:00
/* Free in NULL context */
rc = qmckl_free(QMCKL_NULL_CONTEXT, a);
2021-05-11 16:41:03 +02:00
assert(rc == QMCKL_INVALID_CONTEXT);
2021-03-30 22:40:56 +02:00
/* Free NULL pointer */
rc = qmckl_free(context, NULL);
2021-05-11 16:41:03 +02:00
assert(rc == QMCKL_INVALID_ARG_2);
2021-03-30 22:40:56 +02:00
/* Free for the first time */
2021-03-18 23:55:50 +01:00
rc = qmckl_free(context, a);
2021-05-11 16:41:03 +02:00
assert(rc == QMCKL_SUCCESS);
2021-03-18 23:55:50 +01:00
2021-03-30 22:40:56 +02:00
/* Free again */
rc = qmckl_free(context, a);
2022-08-07 14:57:10 +02:00
assert(rc == QMCKL_INVALID_ARG_2);
2021-03-30 22:40:56 +02:00
/* Clean up */
2021-03-18 23:55:50 +01:00
rc = qmckl_context_destroy(context);
2021-05-11 16:41:03 +02:00
assert(rc == QMCKL_SUCCESS);
2021-02-19 01:39:42 +01:00
2021-03-09 01:16:23 +01:00
#+end_src
2020-10-16 23:56:22 +02:00
2022-08-07 14:57:10 +02:00
* 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.
# Header
#+begin_src c :tangle (eval h_private_func) :noexport
qmckl_exit_code
qmckl_get_malloc_info(qmckl_context context,
const void* pointer,
qmckl_memory_info_struct* info);
#+end_src
# Source
#+begin_src c :tangle (eval c)
qmckl_exit_code
qmckl_get_malloc_info(qmckl_context context,
const void* ptr,
qmckl_memory_info_struct* info)
{
assert (qmckl_context_check(context) != QMCKL_NULL_CONTEXT);
qmckl_context_struct* const ctx = (qmckl_context_struct*) context;
if (ptr == NULL) {
return qmckl_failwith(context,
QMCKL_INVALID_ARG_2,
"qmckl_get_malloc_info",
"Null pointer");
}
if (info == NULL) {
return qmckl_failwith(context,
QMCKL_INVALID_ARG_3,
"qmckl_get_malloc_info",
"Null pointer");
}
qmckl_lock(context);
{
/* Find the pointer entry */
size_t pos = (size_t) 0;
while ( pos < ctx->memory.array_size && ctx->memory.element[pos].pointer != ptr) {
pos += (size_t) 1;
}
if (pos >= ctx->memory.array_size) {
/* Not found */
qmckl_unlock(context);
return qmckl_failwith(context,
QMCKL_INVALID_ARG_2,
"qmckl_get_malloc_info",
"Pointer not found in context");
}
/* Copy info */
memcpy(info, &(ctx->memory.element[pos]), sizeof(qmckl_memory_info_struct));
}
qmckl_unlock(context);
return QMCKL_SUCCESS;
}
#+end_src
# Test :noexport:
#+begin_src c :tangle (eval c_test)
/* Create a context */
context = qmckl_context_create();
info = qmckl_memory_info_struct_zero;
info.size = (size_t) 3*sizeof(int);
/* Allocate an array of ints */
a = (int*) qmckl_malloc(context, info);
/* Check that the size of a is 3*sizeof(int) */
info = qmckl_memory_info_struct_zero;
rc = qmckl_get_malloc_info(context, NULL, &info);
assert (rc == QMCKL_INVALID_ARG_2);
rc = qmckl_get_malloc_info(context, &rc, &info);
assert (rc == QMCKL_INVALID_ARG_2);
rc = qmckl_get_malloc_info(context, a, &info);
assert (rc == QMCKL_SUCCESS);
assert (info.size == 3*sizeof(int));
rc = qmckl_context_destroy(context);
#+end_src
2021-03-09 01:16:23 +01:00
* End of files :noexport:
2020-10-16 23:56:22 +02:00
2021-10-14 21:40:14 +02:00
#+begin_src c :comments org :tangle (eval h_private_func)
#endif
#+end_src
2021-03-30 22:40:56 +02:00
#+begin_src c :comments org :tangle (eval h_private_type)
#endif
#+end_src
2021-03-09 01:16:23 +01:00
** Test
#+begin_src c :comments org :tangle (eval c_test)
2021-05-11 16:41:03 +02:00
return 0;
2020-10-22 01:24:14 +02:00
}
2020-10-17 01:10:54 +02:00
2021-03-09 01:16:23 +01:00
#+end_src
2021-04-30 01:26:19 +02:00
2020-11-05 15:27:25 +01:00
2021-03-09 01:16:23 +01:00
# -*- mode: org -*-
# vim: syntax=c