(git:b77b4be)
Loading...
Searching...
No Matches
dbm_shard.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#include <assert.h>
8#include <omp.h>
9#include <stdbool.h>
10#include <stddef.h>
11#include <stdlib.h>
12#include <string.h>
13
14#include "dbm_hyperparams.h"
15#include "dbm_mempool.h"
16#include "dbm_shard.h"
17
18/*******************************************************************************
19 * \brief Internal routine for finding a power of two greater than given number.
20 * \author Ole Schuett
21 ******************************************************************************/
22static int next_power2(const int start) {
23 int candidate = 2;
24 while (candidate < start) {
25 candidate *= 2;
26 }
27 return candidate;
28}
29
30/*******************************************************************************
31 * \brief Internal routine for finding a prime greater equal than given number.
32 * \author Ole Schuett
33 ******************************************************************************/
34static int next_prime(const int start) {
35 int candidate = start, divisor = 0;
36 while (divisor < candidate) {
37 for (divisor = 2; divisor < candidate; divisor++) {
38 if (candidate % divisor == 0) {
39 candidate++;
40 break;
41 }
42 }
43 }
44 return candidate;
45}
46
47/*******************************************************************************
48 * \brief Internal routine for initializing a shard's hashtable.
49 * \author Ole Schuett
50 ******************************************************************************/
51static void hashtable_init(dbm_shard_t *shard) {
52 // Choosing size as power of two allows to replace modulo with bitwise AND.
53 shard->hashtable_size =
56 shard->hashtable = calloc(shard->hashtable_size, sizeof(int));
57 assert(shard->hashtable != NULL);
58}
59
60/*******************************************************************************
61 * \brief Internal routine for initializing a shard.
62 * \author Ole Schuett
63 ******************************************************************************/
65 shard->nblocks = 0;
66 shard->nblocks_allocated = 0;
67 shard->blocks = NULL;
68 hashtable_init(shard);
69 shard->data_size = 0;
70 shard->data_promised = 0;
71 shard->data_allocated = 0;
72 shard->data = NULL;
73 omp_init_lock(&shard->lock);
74}
75
76/*******************************************************************************
77 * \brief Internal routine for copying content of shard_b into shard_a.
78 * \author Ole Schuett
79 ******************************************************************************/
80void dbm_shard_copy(dbm_shard_t *shard_a, const dbm_shard_t *shard_b) {
81 assert(shard_a != NULL && shard_b != NULL);
82
83 if (shard_a->nblocks_allocated < shard_b->nblocks) {
84 free(shard_a->blocks);
85 shard_a->blocks = malloc(shard_b->nblocks * sizeof(dbm_block_t));
86 shard_a->nblocks_allocated = shard_b->nblocks;
87 assert(shard_a->blocks != NULL);
88 }
89 shard_a->nblocks = shard_b->nblocks;
90
91 if (shard_a->hashtable_size < shard_b->hashtable_size) {
92 free(shard_a->hashtable);
93 shard_a->hashtable = malloc(shard_b->hashtable_size * sizeof(int));
94 assert(shard_a->hashtable != NULL);
95 }
96 shard_a->hashtable_size = shard_b->hashtable_size;
97 shard_a->hashtable_prime = shard_b->hashtable_prime;
98
99 if (shard_a->data_allocated < shard_b->data_size) {
100 dbm_mempool_host_free(shard_a->data);
101 shard_a->data =
102 dbm_mempool_host_malloc(shard_b->data_size * sizeof(double));
103 shard_a->data_allocated = shard_b->data_size;
104 assert(shard_a->data != NULL);
105 }
106 shard_a->data_size = shard_b->data_size;
107
108 if (shard_a->blocks != NULL) {
109 memcpy(shard_a->blocks, shard_b->blocks,
110 shard_b->nblocks * sizeof(dbm_block_t));
111 }
112 if (shard_a->hashtable != NULL) {
113 memcpy(shard_a->hashtable, shard_b->hashtable,
114 shard_b->hashtable_size * sizeof(int));
115 }
116 if (shard_a->data != NULL) {
117 memcpy(shard_a->data, shard_b->data, shard_b->data_size * sizeof(double));
118 }
119}
120
121/*******************************************************************************
122 * \brief Internal routine for releasing a shard.
123 * \author Ole Schuett
124 ******************************************************************************/
126 free(shard->blocks);
127 free(shard->hashtable);
129 omp_destroy_lock(&shard->lock);
130}
131
132/*******************************************************************************
133 * \brief Private hash function based on Cantor pairing function.
134 * https://en.wikipedia.org/wiki/Pairing_function#Cantor_pairing_function
135 * Szudzik's elegant pairing proved to be too asymmetric wrt. row / col.
136 * Using unsigned int to return a positive number even after overflow.
137 * \author Ole Schuett
138 ******************************************************************************/
139static inline unsigned int hash(const unsigned int row,
140 const unsigned int col) {
141 return (row + col) * (row + col + 1) / 2 + row; // Division by 2 is cheap.
142}
143
144/*******************************************************************************
145 * \brief Internal routine for masking a slot in the hash-table.
146 * \author Hans Pabst
147 ******************************************************************************/
148static inline int hashtable_mask(const dbm_shard_t *shard) {
149 return shard->hashtable_size - 1;
150}
151
152/*******************************************************************************
153 * \brief Private routine for inserting a block into a shard's hashtable.
154 * \author Ole Schuett
155 ******************************************************************************/
156static void hashtable_insert(dbm_shard_t *shard, const int block_idx) {
157 assert(0 <= block_idx && block_idx < shard->nblocks);
158 const dbm_block_t *blk = &shard->blocks[block_idx];
159 const unsigned int h = hash(blk->row, blk->col);
160 int slot = (shard->hashtable_prime * h) & hashtable_mask(shard);
161 for (;; slot = (slot + 1) & hashtable_mask(shard)) { // linear probing
162 for (int i = slot; i < shard->hashtable_size; ++i) {
163 if (shard->hashtable[i] == 0) { // 0 means empty
164 shard->hashtable[i] = block_idx + 1; // 1-based
165 return;
166 }
167 }
168 }
169}
170
171/*******************************************************************************
172 * \brief Internal routine for looking up a block from a shard.
173 * \author Ole Schuett
174 ******************************************************************************/
175dbm_block_t *dbm_shard_lookup(const dbm_shard_t *shard, const int row,
176 const int col) {
177 int slot = (shard->hashtable_prime * hash(row, col)) & hashtable_mask(shard);
178 for (;; slot = (slot + 1) & hashtable_mask(shard)) { // linear probing
179 for (int i = slot; i < shard->hashtable_size; ++i) {
180 const int block_idx = shard->hashtable[i];
181 if (block_idx == 0) { // 1-based, 0 means empty
182 return NULL; // block not found
183 }
184 assert(0 < block_idx && block_idx <= shard->nblocks);
185 dbm_block_t *blk = &shard->blocks[block_idx - 1];
186 if (blk->row == row && blk->col == col) {
187 return blk;
188 }
189 }
190 }
191}
192
193/*******************************************************************************
194 * \brief Internal routine for allocating the metadata of a new block.
195 * \author Ole Schuett
196 ******************************************************************************/
198 const int col, const int block_size) {
199 // Grow blocks array if necessary.
200 if (shard->nblocks_allocated < shard->nblocks + 1) {
201 shard->nblocks_allocated = DBM_ALLOCATION_FACTOR * (shard->nblocks + 1);
202 assert((shard->nblocks + 1) <= shard->nblocks_allocated);
203 shard->blocks =
204 realloc(shard->blocks, shard->nblocks_allocated * sizeof(dbm_block_t));
205 assert(shard->blocks != NULL);
206
207 // rebuild hashtable
208 free(shard->hashtable);
209 hashtable_init(shard);
210 for (int i = 0; i < shard->nblocks; i++) {
211 hashtable_insert(shard, i);
212 }
213 }
214
215 const int new_block_idx = shard->nblocks;
216 shard->nblocks++;
217 dbm_block_t *new_block = &shard->blocks[new_block_idx];
218 new_block->row = row;
219 new_block->col = col;
220 new_block->offset = shard->data_promised;
221 shard->data_promised += block_size;
222 // The data_size will be increased after the memory is allocated and zeroed.
223 hashtable_insert(shard, new_block_idx);
224 return new_block;
225}
226
227/*******************************************************************************
228 * \brief Internal routine for allocating and zeroing any promised block's data.
229 * \author Ole Schuett
230 ******************************************************************************/
232
233 // Reallocate data array if necessary.
234 if (shard->data_promised > shard->data_allocated) {
235 const double *data = shard->data;
237 assert(shard->data_promised <= shard->data_allocated);
238 shard->data =
239 dbm_mempool_host_malloc(shard->data_allocated * sizeof(double));
240 assert(shard->data != NULL);
241 memcpy(shard->data, data, shard->data_size * sizeof(double));
243 }
244
245 // Zero new blocks.
246 // The following memset is usually the first touch of the memory, which leads
247 // to frequent page faults. The executing thread determines the NUMA location
248 if (shard->data_promised > shard->data_size) {
249 const int tail = shard->data_promised - shard->data_size;
250 memset(shard->data + shard->data_size, 0, tail * sizeof(double));
251 shard->data_size = shard->data_promised;
252 }
253}
254
255/*******************************************************************************
256 * \brief Internal routine for getting block or promising a new one.
257 * \author Ole Schuett
258 ******************************************************************************/
260 const int col,
261 const int block_size) {
262 dbm_block_t *existing_blk = dbm_shard_lookup(shard, row, col);
263 if (existing_blk != NULL) {
264 return existing_blk;
265 } else {
266 return dbm_shard_promise_new_block(shard, row, col, block_size);
267 }
268}
269
270/*******************************************************************************
271 * \brief Internal routine for getting block or allocating a new one.
272 * \author Ole Schuett
273 ******************************************************************************/
275 const int col,
276 const int block_size) {
277 dbm_block_t *existing_blk = dbm_shard_lookup(shard, row, col);
278 if (existing_blk != NULL) {
279 return existing_blk;
280 }
281
282 // Create a new block.
283 dbm_block_t *new_blk =
284 dbm_shard_promise_new_block(shard, row, col, block_size);
286
287 return new_blk;
288}
289
290// EOF
#define DBM_ALLOCATION_FACTOR
#define DBM_HASHTABLE_FACTOR
void dbm_mempool_host_free(const void *memory)
Internal routine for releasing memory back to the pool.
void * dbm_mempool_host_malloc(size_t size)
Internal routine for allocating host memory from the pool.
static void hashtable_insert(dbm_shard_t *shard, const int block_idx)
Private routine for inserting a block into a shard's hashtable.
Definition dbm_shard.c:156
static void hashtable_init(dbm_shard_t *shard)
Internal routine for initializing a shard's hashtable.
Definition dbm_shard.c:51
static int hashtable_mask(const dbm_shard_t *shard)
Internal routine for masking a slot in the hash-table.
Definition dbm_shard.c:148
void dbm_shard_release(dbm_shard_t *shard)
Internal routine for releasing a shard.
Definition dbm_shard.c:125
static int next_prime(const int start)
Internal routine for finding a prime greater equal than given number.
Definition dbm_shard.c:34
dbm_block_t * dbm_shard_get_or_allocate_block(dbm_shard_t *shard, const int row, const int col, const int block_size)
Internal routine for getting block or allocating a new one.
Definition dbm_shard.c:274
static int next_power2(const int start)
Internal routine for finding a power of two greater than given number.
Definition dbm_shard.c:22
dbm_block_t * dbm_shard_promise_new_block(dbm_shard_t *shard, const int row, const int col, const int block_size)
Internal routine for allocating the metadata of a new block.
Definition dbm_shard.c:197
dbm_block_t * dbm_shard_lookup(const dbm_shard_t *shard, const int row, const int col)
Internal routine for looking up a block from a shard.
Definition dbm_shard.c:175
void dbm_shard_allocate_promised_blocks(dbm_shard_t *shard)
Internal routine for allocating and zeroing any promised block's data.
Definition dbm_shard.c:231
static unsigned int hash(const unsigned int row, const unsigned int col)
Private hash function based on Cantor pairing function. https://en.wikipedia.org/wiki/Pairing_functio...
Definition dbm_shard.c:139
dbm_block_t * dbm_shard_get_or_promise_block(dbm_shard_t *shard, const int row, const int col, const int block_size)
Internal routine for getting block or promising a new one.
Definition dbm_shard.c:259
void dbm_shard_init(dbm_shard_t *shard)
Internal routine for initializing a shard.
Definition dbm_shard.c:64
void dbm_shard_copy(dbm_shard_t *shard_a, const dbm_shard_t *shard_b)
Internal routine for copying content of shard_b into shard_a.
Definition dbm_shard.c:80
static void const int const int i
Internal struct for storing a block's metadata.
Definition dbm_shard.h:20
Internal struct for storing a matrix shard.
Definition dbm_shard.h:30
double * data
Definition dbm_shard.h:43
int * hashtable
Definition dbm_shard.h:37
omp_lock_t lock
Definition dbm_shard.h:45
int nblocks_allocated
Definition dbm_shard.h:32
dbm_block_t * blocks
Definition dbm_shard.h:33
int hashtable_prime
Definition dbm_shard.h:36
int data_allocated
Definition dbm_shard.h:41
int data_size
Definition dbm_shard.h:42
int hashtable_size
Definition dbm_shard.h:35
int data_promised
Definition dbm_shard.h:39