LCOV - code coverage report
Current view: top level - src/dbm - dbm_mempool.c (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:3130539) Lines: 30 36 83.3 %
Date: 2025-05-14 06:57:18 Functions: 6 8 75.0 %

          Line data    Source code
       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             :  ******************************************************************************/
      37             : typedef struct dbm_memchunk {
      38             :   void *mem; // first: allows to cast memchunk into mem-ptr...
      39             :   struct dbm_memchunk *next;
      40             :   size_t size, used;
      41             : } dbm_memchunk_t;
      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
      48             : static dbm_memchunk_t *mempool_device_available_head = NULL;
      49             : static 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
      57             : static dbm_memchunk_t *mempool_host_available_head = NULL;
      58             : static dbm_memchunk_t *mempool_host_allocated_head = NULL;
      59             : #endif
      60             : 
      61             : /*******************************************************************************
      62             :  * \brief Private statistics.
      63             :  * \author Hans Pabst
      64             :  ******************************************************************************/
      65             : static dbm_memstats_t mempool_stats = {0};
      66             : 
      67             : /*******************************************************************************
      68             :  * \brief Private routine for actually allocating system memory.
      69             :  * \author Ole Schuett
      70             :  ******************************************************************************/
      71     2818394 : static void *actual_malloc(size_t size, bool on_device) {
      72     2818394 :   void *memory = NULL;
      73             : 
      74     2818394 :   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     2765303 :     (void)on_device; // mark used
      89     2765303 :     memory = dbm_mpi_alloc_mem(size);
      90             : #endif
      91     2765303 :     assert(memory != NULL);
      92             : 
      93             :     // Update statistics.
      94     2765303 :     if (on_device) {
      95           0 : #pragma omp atomic
      96             :       ++mempool_stats.device_mallocs;
      97             :     } else {
      98     2765303 : #pragma omp atomic
      99             :       ++mempool_stats.host_mallocs;
     100             :     }
     101             :   }
     102             : 
     103     2818394 :   return memory;
     104             : }
     105             : 
     106             : /*******************************************************************************
     107             :  * \brief Private routine for actually freeing system memory.
     108             :  * \author Ole Schuett
     109             :  ******************************************************************************/
     110     4433577 : static void actual_free(const void *memory, bool on_device) {
     111     4433577 :   if (NULL != memory) {
     112     2765303 :     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
     122             :       dbm_mpi_free_mem(mem);
     123             : #endif
     124             :     }
     125             : #else
     126     2765303 :     (void)on_device; // mark used
     127     2765303 :     dbm_mpi_free_mem(mem);
     128             : #endif
     129             :   }
     130     4433577 : }
     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
     137             : static 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             :  ******************************************************************************/
     206     2818394 : void *dbm_mempool_host_malloc(size_t size) {
     207             : #if DBM_MEMPOOL_HOST_ENABLED
     208             :   return internal_mempool_malloc(&mempool_host_available_head,
     209             :                                  &mempool_host_allocated_head, size);
     210             : #else
     211     2818394 :   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             :  ******************************************************************************/
     219           0 : void *dbm_mempool_device_malloc(size_t size) {
     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
     224           0 :   return dbm_mempool_host_malloc(size);
     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
     235             : static 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             :  ******************************************************************************/
     263     4433577 : void 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     4433577 :   actual_free(memory, false);
     269             : #endif
     270     4433577 : }
     271             : 
     272             : /*******************************************************************************
     273             :  * \brief Internal routine for releasing memory back to the pool.
     274             :  * \author Ole Schuett
     275             :  ******************************************************************************/
     276           0 : void 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           0 :   dbm_mempool_host_free(memory);
     282             : #else
     283             :   actual_free(memory, true);
     284             : #endif
     285           0 : }
     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
     292             : static 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             :  ******************************************************************************/
     316        9162 : void dbm_mempool_clear(void) {
     317        9162 : #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        9162 :   }
     328        9162 : }
     329             : 
     330             : /*******************************************************************************
     331             :  * \brief Internal routine to query statistics.
     332             :  * \author Hans Pabst
     333             :  ******************************************************************************/
     334        9280 : void dbm_mempool_statistics(dbm_memstats_t *memstats) {
     335        9280 :   assert(NULL != memstats);
     336        9280 :   memset(memstats, 0, sizeof(dbm_memstats_t));
     337        9280 : #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) {
     351             :       mempool_stats.device_used = memstats->device_used;
     352             :     }
     353             :     if (mempool_stats.device_size < memstats->device_size) {
     354             :       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) {
     369             :       mempool_stats.host_used = memstats->host_used;
     370             :     }
     371             :     if (mempool_stats.host_size < memstats->host_size) {
     372             :       mempool_stats.host_size = memstats->host_size;
     373             :     }
     374             : #endif
     375        9280 :     memcpy(memstats, &mempool_stats, sizeof(dbm_memstats_t));
     376             :   }
     377        9280 : }
     378             : 
     379             : // EOF

Generated by: LCOV version 1.15