(git:e5b1968)
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#if defined(__OFFLOAD) && !defined(__NO_OFFLOAD_DBM)
23#define DBM_MEMPOOL_OFFLOAD_ENABLED 1
24#else
25#define DBM_MEMPOOL_OFFLOAD_ENABLED 0
26#endif
27#define DBM_MEMPOOL_DEVICE_ENABLED \
28 (DBM_MEMPOOL_DEVICE && DBM_MEMPOOL_OFFLOAD_ENABLED)
29#define DBM_MEMPOOL_HOST_ENABLED \
30 ((DBM_MEMPOOL_HOST && DBM_ALLOC_OFFLOAD && DBM_MEMPOOL_OFFLOAD_ENABLED) || \
31 (1 < DBM_MEMPOOL_HOST))
32
33/*******************************************************************************
34 * \brief Private struct for storing a chunk of memory.
35 * \author Ole Schuett
36 ******************************************************************************/
37typedef struct dbm_memchunk {
38 void *mem; // first: allows to cast memchunk into mem-ptr...
40 size_t size, used;
42
43/*******************************************************************************
44 * \brief Private single-linked lists of memory chunks available and allocated.
45 * \author Ole Schuett
46 ******************************************************************************/
47#if DBM_MEMPOOL_DEVICE_ENABLED
48static dbm_memchunk_t *mempool_device_available_head = NULL;
49static dbm_memchunk_t *mempool_device_allocated_head = NULL;
50#endif
51
52/*******************************************************************************
53 * \brief Private single-linked lists of memory chunks available and allocated.
54 * \author Ole Schuett
55 ******************************************************************************/
56#if DBM_MEMPOOL_HOST_ENABLED
57static dbm_memchunk_t *mempool_host_available_head = NULL;
58static dbm_memchunk_t *mempool_host_allocated_head = NULL;
59#endif
60
61/*******************************************************************************
62 * \brief Private statistics.
63 * \author Hans Pabst
64 ******************************************************************************/
66
67/*******************************************************************************
68 * \brief Private routine for actually allocating system memory.
69 * \author Ole Schuett
70 ******************************************************************************/
71static void *actual_malloc(size_t size, bool on_device) {
72 void *memory = NULL;
73
74 if (0 != size) {
75#if DBM_MEMPOOL_OFFLOAD_ENABLED
76 if (on_device) {
77 offload_activate_chosen_device();
78 offloadMalloc(&memory, size);
79 } else {
80#if DBM_ALLOC_OFFLOAD
81 offload_activate_chosen_device();
82 offloadMallocHost(&memory, size);
83#else
84 memory = dbm_mpi_alloc_mem(size);
85#endif
86 }
87#else
88 (void)on_device; // mark used
89 memory = dbm_mpi_alloc_mem(size);
90#endif
91 assert(memory != NULL);
92
93 // Update statistics.
94 if (on_device) {
95#pragma omp atomic
97 } else {
98#pragma omp atomic
100 }
101 }
102
103 return memory;
104}
105
106/*******************************************************************************
107 * \brief Private routine for actually freeing system memory.
108 * \author Ole Schuett
109 ******************************************************************************/
110static void actual_free(const void *memory, bool on_device) {
111 if (NULL != memory) {
112 void *mem = (void *)(uintptr_t)memory;
113#if DBM_MEMPOOL_OFFLOAD_ENABLED
114 if (on_device) {
115 offload_activate_chosen_device();
116 offloadFree(mem);
117 } else {
118#if DBM_ALLOC_OFFLOAD
119 offload_activate_chosen_device();
120 offloadFreeHost(mem);
121#else
123#endif
124 }
125#else
126 (void)on_device; // mark used
128#endif
129 }
130}
131
132/*******************************************************************************
133 * \brief Private routine for allocating host or device memory from the pool.
134 * \author Ole Schuett
135 ******************************************************************************/
136#if DBM_MEMPOOL_DEVICE_ENABLED || DBM_MEMPOOL_HOST_ENABLED
137static void *internal_mempool_malloc(dbm_memchunk_t **available_head,
138 dbm_memchunk_t **allocated_head,
139 size_t size) {
140 if (size == 0) {
141 return NULL;
142 }
143
144 dbm_memchunk_t *chunk = NULL;
145#if DBM_MEMPOOL_DEVICE_ENABLED
146 const bool on_device = (&mempool_device_available_head == available_head);
147#else
148 const bool on_device = false;
149#endif
150#if DBM_MEMPOOL_HOST_ENABLED
151 assert(on_device || &mempool_host_available_head == available_head);
152 assert(on_device || &mempool_host_allocated_head == allocated_head);
153#endif
154
155#pragma omp critical(dbm_mempool_modify)
156 {
157 // Find a suitable chunk in mempool_available.
158 dbm_memchunk_t **reuse = NULL, **reclaim = NULL;
159 for (; NULL != *available_head; available_head = &(*available_head)->next) {
160 const size_t s = (*available_head)->size;
161 if (size <= s && (NULL == reuse || s < (*reuse)->size)) {
162 reuse = available_head;
163 if (size == (*reuse)->size) {
164 break; // early exit
165 }
166 } else if (NULL == reclaim || s > (*reclaim)->size) {
167 reclaim = available_head;
168 }
169 }
170 if (NULL == reuse) {
171 reuse = reclaim;
172 }
173
174 // Remove chunk from mempool_available.
175 if (NULL != reuse) {
176 chunk = *reuse;
177 *reuse = chunk->next;
178 } else { // Allocate a new chunk.
179 chunk = calloc(1, sizeof(dbm_memchunk_t));
180 assert(chunk != NULL);
181 }
182
183 // Insert chunk into mempool_allocated.
184 chunk->next = *allocated_head;
185 *allocated_head = chunk;
186 }
187
188 // Resize chunk (not in critical section).
189 if (chunk->size < size) {
190 void *memory = chunk->mem;
191 chunk->mem = NULL; // race ok (free and stats)
192 actual_free(memory, on_device);
193 chunk->mem = actual_malloc(size, on_device);
194 chunk->size = size;
195 }
196 chunk->used = size; // stats
197
198 return chunk->mem;
199}
200#endif
201
202/*******************************************************************************
203 * \brief Internal routine for allocating host memory from the pool.
204 * \author Ole Schuett
205 ******************************************************************************/
207#if DBM_MEMPOOL_HOST_ENABLED
208 return internal_mempool_malloc(&mempool_host_available_head,
209 &mempool_host_allocated_head, size);
210#else
211 return actual_malloc(size, false);
212#endif
213}
214
215/*******************************************************************************
216 * \brief Internal routine for allocating device memory from the pool
217 * \author Ole Schuett
218 ******************************************************************************/
220#if DBM_MEMPOOL_DEVICE_ENABLED
221 return internal_mempool_malloc(&mempool_device_available_head,
222 &mempool_device_allocated_head, size);
223#elif DBM_MEMPOOL_DEVICE
225#else
226 return actual_malloc(size, true);
227#endif
228}
229
230/*******************************************************************************
231 * \brief Private routine for releasing memory back to the pool.
232 * \author Ole Schuett
233 ******************************************************************************/
234#if DBM_MEMPOOL_DEVICE_ENABLED || DBM_MEMPOOL_HOST_ENABLED
235static void internal_mempool_free(dbm_memchunk_t **available_head,
236 dbm_memchunk_t **allocated_head,
237 const void *mem) {
238 if (NULL != mem) {
239#pragma omp critical(dbm_mempool_modify)
240 {
241 // Find chunk in allocated chunks.
242 while (NULL != *allocated_head && (*allocated_head)->mem != mem) {
243 allocated_head = &(*allocated_head)->next;
244 }
245 dbm_memchunk_t *chunk = *allocated_head;
246 assert(NULL != chunk && chunk->mem == mem);
247
248 // Remove chunk from mempool_allocated.
249 *allocated_head = chunk->next;
250
251 // Add chunk to mempool_available.
252 chunk->next = *available_head;
253 *available_head = chunk;
254 }
255 }
256}
257#endif
258
259/*******************************************************************************
260 * \brief Internal routine for releasing memory back to the pool.
261 * \author Ole Schuett
262 ******************************************************************************/
263void dbm_mempool_host_free(const void *memory) {
264#if DBM_MEMPOOL_HOST_ENABLED
265 internal_mempool_free(&mempool_host_available_head,
266 &mempool_host_allocated_head, memory);
267#else
268 actual_free(memory, false);
269#endif
270}
271
272/*******************************************************************************
273 * \brief Internal routine for releasing memory back to the pool.
274 * \author Ole Schuett
275 ******************************************************************************/
276void dbm_mempool_device_free(const void *memory) {
277#if DBM_MEMPOOL_DEVICE_ENABLED
278 internal_mempool_free(&mempool_device_available_head,
279 &mempool_device_allocated_head, memory);
280#elif DBM_MEMPOOL_DEVICE
281 dbm_mempool_host_free(memory);
282#else
283 actual_free(memory, true);
284#endif
285}
286
287/*******************************************************************************
288 * \brief Private routine for freeing all memory in the pool.
289 * \author Ole Schuett
290 ******************************************************************************/
291#if DBM_MEMPOOL_DEVICE_ENABLED || DBM_MEMPOOL_HOST_ENABLED
292static void internal_mempool_clear(dbm_memchunk_t **available_head) {
293#if DBM_MEMPOOL_DEVICE_ENABLED
294 const bool on_device = (&mempool_device_available_head == available_head);
295#else
296 const bool on_device = false;
297#endif
298#if DBM_MEMPOOL_HOST_ENABLED
299 assert(on_device || &mempool_host_available_head == available_head);
300#endif
301
302 // Free chunks in mempool_available.
303 while (NULL != *available_head) {
304 dbm_memchunk_t *chunk = *available_head;
305 *available_head = chunk->next; // remove chunk
306 actual_free(chunk->mem, on_device);
307 free(chunk);
308 }
309}
310#endif
311
312/*******************************************************************************
313 * \brief Internal routine for freeing all memory in the pool.
314 * \author Ole Schuett
315 ******************************************************************************/
317#pragma omp critical(dbm_mempool_modify)
318 {
319#if DBM_MEMPOOL_DEVICE_ENABLED
320 assert(mempool_device_allocated_head == NULL); // check for leak
321 internal_mempool_clear(&mempool_device_available_head);
322#endif
323#if DBM_MEMPOOL_HOST_ENABLED
324 assert(mempool_host_allocated_head == NULL); // check for leak
325 internal_mempool_clear(&mempool_host_available_head);
326#endif
327 }
328}
329
330/*******************************************************************************
331 * \brief Internal routine to query statistics.
332 * \author Hans Pabst
333 ******************************************************************************/
335 assert(NULL != memstats);
336 memset(memstats, 0, sizeof(dbm_memstats_t));
337#pragma omp critical(dbm_mempool_modify)
338 {
339#if DBM_MEMPOOL_DEVICE_ENABLED
340 for (dbm_memchunk_t *chunk = mempool_device_available_head; NULL != chunk;
341 chunk = chunk->next) {
342 memstats->device_used += chunk->used;
343 memstats->device_size += chunk->size;
344 }
345 for (dbm_memchunk_t *chunk = mempool_device_allocated_head; NULL != chunk;
346 chunk = chunk->next) {
347 memstats->device_used += chunk->used;
348 memstats->device_size += chunk->size;
349 }
350 if (mempool_stats.device_used < memstats->device_used) {
352 }
353 if (mempool_stats.device_size < memstats->device_size) {
355 }
356#endif
357#if DBM_MEMPOOL_HOST_ENABLED
358 for (dbm_memchunk_t *chunk = mempool_host_available_head; NULL != chunk;
359 chunk = chunk->next) {
360 memstats->host_used += chunk->used;
361 memstats->host_size += chunk->size;
362 }
363 for (dbm_memchunk_t *chunk = mempool_host_allocated_head; NULL != chunk;
364 chunk = chunk->next) {
365 memstats->host_used += chunk->used;
366 memstats->host_size += chunk->size;
367 }
368 if (mempool_stats.host_used < memstats->host_used) {
370 }
371 if (mempool_stats.host_size < memstats->host_size) {
373 }
374#endif
375 memcpy(memstats, &mempool_stats, sizeof(dbm_memstats_t));
376 }
377}
378
379// EOF
static void * actual_malloc(size_t size, bool on_device)
Private routine for actually allocating system memory.
Definition dbm_mempool.c:71
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.
void dbm_mempool_host_free(const void *memory)
Private routine for releasing memory back to the pool.
void dbm_mempool_clear(void)
Private 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 single-linked lists of memory chunks available and allocated.
Definition dbm_mempool.c:65
void * dbm_mempool_host_malloc(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.
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:37
struct dbm_memchunk * next
Definition dbm_mempool.c:39
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