(git:33f85d8)
Loading...
Searching...
No Matches
dbm_mempool.c
Go to the documentation of this file.
1/*----------------------------------------------------------------------------*/
2/* CP2K: A general program to perform molecular dynamics simulations */
3/* Copyright 2000-2025 CP2K developers group <https://cp2k.org> */
4/* */
5/* SPDX-License-Identifier: BSD-3-Clause */
6/*----------------------------------------------------------------------------*/
7
8#include <assert.h>
9#include <omp.h>
10#include <stdbool.h>
11#include <stddef.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15
16#include "../offload/offload_library.h"
17#include "../offload/offload_runtime.h"
18#include "dbm_hyperparams.h"
19#include "dbm_mempool.h"
20#include "dbm_mpi.h"
21
22/*******************************************************************************
23 * \brief Private struct for storing a chunk of memory.
24 * \author Ole Schuett
25 ******************************************************************************/
26typedef struct dbm_memchunk {
27 void *mem; // first: allows to cast memchunk into mem-ptr...
29 size_t size, used;
31
32/*******************************************************************************
33 * \brief Private linked list of memory chunks that are available.
34 * \author Ole Schuett
35 ******************************************************************************/
38
39/*******************************************************************************
40 * \brief Private linked list of memory chunks that are in use.
41 * \author Ole Schuett
42 ******************************************************************************/
45
46/*******************************************************************************
47 * \brief Private statistics.
48 * \author Hans Pabst
49 ******************************************************************************/
51
52/*******************************************************************************
53 * \brief Private routine for actually allocating system memory.
54 * \author Ole Schuett
55 ******************************************************************************/
56static void *actual_malloc(size_t size, bool on_device) {
57 void *memory = NULL;
58
59 if (0 != size) {
60#if defined(__OFFLOAD) && !defined(__NO_OFFLOAD_DBM)
61 if (on_device) {
62 offload_activate_chosen_device();
63 offloadMalloc(&memory, size);
64 } else {
65#if DBM_ALLOC_OFFLOAD
66 offloadMallocHost(&memory, size);
67#else
68 memory = dbm_mpi_alloc_mem(size);
69#endif
70 }
71#else
72 (void)on_device; // mark used
73 memory = dbm_mpi_alloc_mem(size);
74#endif
75 assert(memory != NULL);
76
77 // Update statistics.
78 if (on_device) {
79#pragma omp atomic
81 } else {
82#pragma omp atomic
84 }
85 }
86
87 return memory;
88}
89
90/*******************************************************************************
91 * \brief Private routine for actually freeing system memory.
92 * \author Ole Schuett
93 ******************************************************************************/
94static void actual_free(const void *memory, bool on_device) {
95 if (NULL != memory) {
96 void *mem = (void *)(uintptr_t)memory;
97#if defined(__OFFLOAD) && !defined(__NO_OFFLOAD_DBM)
98 if (on_device) {
99 offload_activate_chosen_device();
100 offloadFree(mem);
101 } else {
102#if DBM_ALLOC_OFFLOAD
103 offloadFreeHost(mem);
104#else
106#endif
107 }
108#else
109 (void)on_device; // mark used
111#endif
112 }
113}
114
115/*******************************************************************************
116 * \brief Private routine for allocating host or device memory from the pool.
117 * \author Ole Schuett
118 ******************************************************************************/
119#if DBM_MEMPOOL_DEVICE || DBM_MEMPOOL_HOST
120static void *internal_mempool_malloc(dbm_memchunk_t **available_head,
121 dbm_memchunk_t **allocated_head,
122 size_t size) {
123 if (size == 0) {
124 return NULL;
125 }
126
127 dbm_memchunk_t *chunk = NULL;
128 const bool on_device = (&mempool_device_available_head == available_head);
129 assert(on_device || &mempool_host_available_head == available_head);
130 assert(on_device || &mempool_host_allocated_head == allocated_head);
131
132#pragma omp critical(dbm_mempool_modify)
133 {
134 // Find a suitable chunk in mempool_available.
135 dbm_memchunk_t **reuse = NULL, **reclaim = NULL;
136 for (; NULL != *available_head; available_head = &(*available_head)->next) {
137 const size_t s = (*available_head)->size;
138 if (size <= s && (NULL == reuse || s < (*reuse)->size)) {
139 reuse = available_head;
140 if (size == (*reuse)->size) {
141 break; // exact match
142 }
143 } else if (NULL != reclaim) {
144 if (s > (*reclaim)->size) {
145 reclaim = available_head;
146 }
147 } else {
148 reclaim = available_head;
149 }
150 }
151 if (NULL == reuse) {
152 reuse = reclaim;
153 }
154
155 // Remove chunk from mempool_available.
156 if (NULL != reuse) {
157 chunk = *reuse;
158 *reuse = chunk->next;
159 } else { // Allocate a new chunk.
160 chunk = calloc(1, sizeof(dbm_memchunk_t));
161 assert(chunk != NULL);
162 }
163
164 // Insert chunk into mempool_allocated.
165 chunk->next = *allocated_head;
166 *allocated_head = chunk;
167 }
168
169 // Resize chunk (not in critical section).
170 if (chunk->size < size) {
171 void *memory = chunk->mem;
172 chunk->mem = NULL; // race ok (free and stats)
173 actual_free(memory, on_device);
174 chunk->mem = actual_malloc(size, on_device);
175 chunk->size = size;
176 }
177 chunk->used = size; // stats
178
179 return chunk->mem;
180}
181#endif
182
183/*******************************************************************************
184 * \brief Internal routine for allocating host memory from the pool.
185 * \author Ole Schuett
186 ******************************************************************************/
188#if DBM_MEMPOOL_HOST
191#else
192 return actual_malloc(size, false);
193#endif
194}
195
196/*******************************************************************************
197 * \brief Internal routine for allocating device memory from the pool
198 * \author Ole Schuett
199 ******************************************************************************/
201#if DBM_MEMPOOL_DEVICE
202#if defined(__OFFLOAD) && !defined(__NO_OFFLOAD_DBM)
205#else
207#endif
208#else
209 return actual_malloc(size, true);
210#endif
211}
212
213/*******************************************************************************
214 * \brief Private routine for releasing memory back to the pool.
215 * \author Ole Schuett
216 ******************************************************************************/
217#if DBM_MEMPOOL_DEVICE || DBM_MEMPOOL_HOST
218static void internal_mempool_free(dbm_memchunk_t **available_head,
219 dbm_memchunk_t **allocated_head,
220 const void *mem) {
221 if (NULL != mem) {
222#pragma omp critical(dbm_mempool_modify)
223 {
224 // Find chunk in allocated chunks.
225 while (NULL != *allocated_head && (*allocated_head)->mem != mem) {
226 allocated_head = &(*allocated_head)->next;
227 }
228 dbm_memchunk_t *chunk = *allocated_head;
229 assert(NULL != chunk && chunk->mem == mem);
230
231 // Remove chunk from mempool_allocated.
232 *allocated_head = chunk->next;
233
234 // Add chunk to mempool_available.
235 chunk->next = *available_head;
236 *available_head = chunk;
237 }
238 }
239}
240#endif
241
242/*******************************************************************************
243 * \brief Internal routine for releasing memory back to the pool.
244 * \author Ole Schuett
245 ******************************************************************************/
246void dbm_mempool_host_free(const void *memory) {
247#if DBM_MEMPOOL_HOST
250#else
251 actual_free(memory, false);
252#endif
253}
254
255/*******************************************************************************
256 * \brief Internal routine for releasing memory back to the pool.
257 * \author Ole Schuett
258 ******************************************************************************/
259void dbm_mempool_device_free(const void *memory) {
260#if DBM_MEMPOOL_DEVICE
261#if defined(__OFFLOAD) && !defined(__NO_OFFLOAD_DBM)
264#else
265 dbm_mempool_host_free(memory);
266#endif
267#else
268 actual_free(memory, true);
269#endif
270}
271
272/*******************************************************************************
273 * \brief Private routine for freeing all memory in the pool.
274 * \author Ole Schuett
275 ******************************************************************************/
276static void internal_mempool_clear(dbm_memchunk_t **available_head) {
277 const bool on_device = (&mempool_device_available_head == available_head);
278 assert(on_device || &mempool_host_available_head == available_head);
279
280 // Free chunks in mempool_available.
281 while (NULL != *available_head) {
282 dbm_memchunk_t *chunk = *available_head;
283 *available_head = chunk->next; // remove chunk
284 actual_free(chunk->mem, on_device);
285 free(chunk);
286 }
287}
288
289/*******************************************************************************
290 * \brief Internal routine for freeing all memory in the pool.
291 * \author Ole Schuett
292 ******************************************************************************/
294 // check for memory leak
295 assert(mempool_device_allocated_head == NULL);
296 assert(mempool_host_allocated_head == NULL);
297
298#pragma omp critical(dbm_mempool_modify)
299 {
302 }
303}
304
305/*******************************************************************************
306 * \brief Internal routine to query statistics.
307 * \author Hans Pabst
308 ******************************************************************************/
310 assert(NULL != memstats);
311 memset(memstats, 0, sizeof(dbm_memstats_t));
312#pragma omp critical(dbm_mempool_modify)
313 {
314 dbm_memchunk_t *chunk;
315 for (chunk = mempool_device_available_head; NULL != chunk;
316 chunk = chunk->next) {
317 memstats->device_used += chunk->used;
318 memstats->device_size += chunk->size;
319 }
320 for (chunk = mempool_device_allocated_head; NULL != chunk;
321 chunk = chunk->next) {
322 memstats->device_used += chunk->used;
323 memstats->device_size += chunk->size;
324 }
325 for (chunk = mempool_host_available_head; NULL != chunk;
326 chunk = chunk->next) {
327 memstats->host_used += chunk->used;
328 memstats->host_size += chunk->size;
329 }
330 for (chunk = mempool_host_allocated_head; NULL != chunk;
331 chunk = chunk->next) {
332 memstats->host_used += chunk->used;
333 memstats->host_size += chunk->size;
334 }
335 if (mempool_stats.device_used < memstats->device_used) {
337 }
338 if (mempool_stats.device_size < memstats->device_size) {
340 }
341 if (mempool_stats.host_used < memstats->host_used) {
343 }
344 if (mempool_stats.host_size < memstats->host_size) {
346 }
347 memcpy(memstats, &mempool_stats, sizeof(dbm_memstats_t));
348 }
349}
350
351// EOF
static void * actual_malloc(size_t size, bool on_device)
Private routine for actually allocating system memory.
Definition dbm_mempool.c:56
void dbm_mempool_device_free(const void *memory)
Internal routine for releasing memory back to the pool.
void dbm_mempool_statistics(dbm_memstats_t *memstats)
Internal routine to query statistics.
static dbm_memchunk_t * mempool_device_available_head
Private linked list of memory chunks that are available.
Definition dbm_mempool.c:36
void dbm_mempool_host_free(const void *memory)
Internal routine for releasing memory back to the pool.
void dbm_mempool_clear(void)
Internal routine for freeing all memory in the pool.
struct dbm_memchunk dbm_memchunk_t
Private struct for storing a chunk of memory.
static dbm_memstats_t mempool_stats
Private statistics.
Definition dbm_mempool.c:50
static void internal_mempool_clear(dbm_memchunk_t **available_head)
Private routine for freeing all memory in the pool.
void * dbm_mempool_host_malloc(size_t size)
Internal routine for allocating host memory from the pool.
static dbm_memchunk_t * mempool_host_available_head
Definition dbm_mempool.c:37
static void * internal_mempool_malloc(dbm_memchunk_t **available_head, dbm_memchunk_t **allocated_head, size_t size)
Private routine for allocating host or device memory from the pool.
static void actual_free(const void *memory, bool on_device)
Private routine for actually freeing system memory.
Definition dbm_mempool.c:94
static void internal_mempool_free(dbm_memchunk_t **available_head, dbm_memchunk_t **allocated_head, const void *mem)
Private routine for releasing memory back to the pool.
static dbm_memchunk_t * mempool_host_allocated_head
Definition dbm_mempool.c:44
static dbm_memchunk_t * mempool_device_allocated_head
Private linked list of memory chunks that are in use.
Definition dbm_mempool.c:43
void * dbm_mempool_device_malloc(size_t size)
Internal routine for allocating device memory from the pool.
void dbm_mpi_free_mem(void *mem)
Wrapper around MPI_Free_mem.
Definition dbm_mpi.c:499
void * dbm_mpi_alloc_mem(size_t size)
Wrapper around MPI_Alloc_mem.
Definition dbm_mpi.c:483
Private struct for storing a chunk of memory.
Definition dbm_mempool.c:26
struct dbm_memchunk * next
Definition dbm_mempool.c:28
Internal struct for pool statistics.
Definition dbm_mempool.h:50
uint64_t host_size
Definition dbm_mempool.h:54
uint64_t device_used
Definition dbm_mempool.h:52
uint64_t host_mallocs
Definition dbm_mempool.h:56
uint64_t host_used
Definition dbm_mempool.h:52
uint64_t device_mallocs
Definition dbm_mempool.h:56
uint64_t device_size
Definition dbm_mempool.h:54