NVMLib  very early alpha
A library to optimally use a Hybrid RAM setup.
hashmap_tx.c
Go to the documentation of this file.
1 
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <errno.h>
10 #include <inttypes.h>
11 #include "hashmap_tx.h"
12 
13 /* large prime number used as a hashing function coefficient */
14 #define HASH_FUNC_COEFF_P 32212254719ULL
15 
16 /* initial number of buckets */
17 #define INIT_BUCKETS_NUM 10
18 
19 /* number of values in a bucket which trigger hashtable rebuild check */
20 #define MIN_HASHSET_THRESHOLD 5
21 
22 /* number of values in a bucket which force hashtable rebuild */
23 #define MAX_HASHSET_THRESHOLD 10
24 
37 static void
38 create_hashmap(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap, uint32_t seed)
39 {
40  size_t len = INIT_BUCKETS_NUM;
41  size_t sz = sizeof(struct buckets) +
42  len * sizeof(TOID(struct entry));
43 
44  TX_BEGIN(pop) {
45  TX_ADD(hashmap);
46 
47  D_RW(hashmap)->seed = seed;
48  do {
49  D_RW(hashmap)->hash_fun_a = (uint32_t)rand();
50  } while (D_RW(hashmap)->hash_fun_a == 0);
51  D_RW(hashmap)->hash_fun_b = (uint32_t)rand();
52  D_RW(hashmap)->hash_fun_p = HASH_FUNC_COEFF_P;
53 
54  D_RW(hashmap)->buckets = TX_ZALLOC(struct buckets, sz);
55  D_RW(D_RW(hashmap)->buckets)->nbuckets = len;
56  } TX_ONABORT {
57  fprintf(stderr, "%s: create transaction aborted: %s\n", __func__,
58  pmemobj_errormsg());
59  abort();
60  } TX_END
61 }
62 
75 static uint64_t
76 hash(const TOID(struct hashmap_tx) *hashmap,
77  const TOID(struct buckets) *buckets, uint64_t value)
78 {
79  uint32_t a = D_RO(*hashmap)->hash_fun_a;
80  uint32_t b = D_RO(*hashmap)->hash_fun_b;
81  uint64_t p = D_RO(*hashmap)->hash_fun_p;
82  size_t len = D_RO(*buckets)->nbuckets;
83 
84  return ((a * value + b) % p) % len;
85 }
86 
101 static void
102 hm_tx_rebuild(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap, size_t new_len)
103 {
104  TOID(struct buckets) buckets_old = D_RO(hashmap)->buckets;
105 
106  if (new_len == 0)
107  new_len = D_RO(buckets_old)->nbuckets;
108 
109  size_t sz_old = sizeof(struct buckets) +
110  D_RO(buckets_old)->nbuckets *
111  sizeof(TOID(struct entry));
112  size_t sz_new = sizeof(struct buckets) +
113  new_len * sizeof(TOID(struct entry));
114 
115  TX_BEGIN(pop) {
116  TX_ADD_FIELD(hashmap, buckets);
117  TOID(struct buckets) buckets_new =
118  TX_ZALLOC(struct buckets, sz_new);
119  D_RW(buckets_new)->nbuckets = new_len;
120  pmemobj_tx_add_range(buckets_old.oid, 0, sz_old);
121 
122  for (size_t i = 0; i < D_RO(buckets_old)->nbuckets; ++i) {
123  while (!TOID_IS_NULL(D_RO(buckets_old)->bucket[i])) {
124  TOID(struct entry) en =
125  D_RO(buckets_old)->bucket[i];
126  uint64_t h = hash(&hashmap, &buckets_new,
127  D_RO(en)->key);
128 
129  D_RW(buckets_old)->bucket[i] = D_RO(en)->next;
130 
131  TX_ADD_FIELD(en, next);
132  D_RW(en)->next = D_RO(buckets_new)->bucket[h];
133  D_RW(buckets_new)->bucket[h] = en;
134  }
135  }
136 
137  D_RW(hashmap)->buckets = buckets_new;
138  TX_FREE(buckets_old);
139  } TX_ONABORT {
140  fprintf(stderr, "%s: rebuild transaction aborted: %s\n", __func__,
141  pmemobj_errormsg());
142  /*
143  * We don't need to do anything here, because everything is
144  * consistent. The only thing affected is performance.
145  */
146  } TX_END
147 
148 }
149 
163 int
164 hm_tx_insert(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap,
165  uint64_t key, MEMoid value)
166 {
167  TOID(struct buckets) buckets = D_RO(hashmap)->buckets;
168  TOID(struct entry) var;
169 
170  uint64_t h = hash(&hashmap, &buckets, key);
171  int num = 0;
172 
173  for (var = D_RO(buckets)->bucket[h];
174  !TOID_IS_NULL(var);
175  var = D_RO(var)->next) {
176  if (D_RO(var)->key == key)
177  return 1;
178  num++;
179  }
180 
181  int ret = 0;
182  TX_BEGIN(pop) {
183  TX_ADD_FIELD(D_RO(hashmap)->buckets, bucket[h]);
184  TX_ADD_FIELD(hashmap, count);
185 
186  TOID(struct entry) e = TX_NEW(struct entry);
187  D_RW(e)->key = key;
188  D_RW(e)->value = value;
189  D_RW(e)->next = D_RO(buckets)->bucket[h];
190  D_RW(buckets)->bucket[h] = e;
191 
192  D_RW(hashmap)->count++;
193  num++;
194  } TX_ONABORT {
195  fprintf(stderr, "insert transaction aborted: %s\n",
196  pmemobj_errormsg());
197  ret = -1;
198  } TX_END
199 
200  if (ret)
201  return ret;
202 
203  if (num > MAX_HASHSET_THRESHOLD ||
204  (num > MIN_HASHSET_THRESHOLD &&
205  D_RO(hashmap)->count > 2 * D_RO(buckets)->nbuckets))
206  hm_tx_rebuild(pop, hashmap, D_RO(buckets)->nbuckets * 2);
207 
208  return 0;
209 }
210 
223 MEMoid
224 hm_tx_remove(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap, uint64_t key)
225 {
226  TOID(struct buckets) buckets = D_RO(hashmap)->buckets;
227  TOID(struct entry) var, prev = TOID_NULL(struct entry);
228 
229  uint64_t h = hash(&hashmap, &buckets, key);
230  for (var = D_RO(buckets)->bucket[h];
231  !TOID_IS_NULL(var);
232  prev = var, var = D_RO(var)->next) {
233  if (D_RO(var)->key == key)
234  break;
235  }
236 
237  if (TOID_IS_NULL(var))
238  return MEMOID_NULL;
239  int ret = 0;
240 
241  MEMoid retoid = D_RO(var)->value;
242  TX_BEGIN(pop) {
243  if (TOID_IS_NULL(prev))
244  TX_ADD_FIELD(D_RO(hashmap)->buckets, bucket[h]);
245  else
246  TX_ADD_FIELD(prev, next);
247  TX_ADD_FIELD(hashmap, count);
248 
249  if (TOID_IS_NULL(prev))
250  D_RW(buckets)->bucket[h] = D_RO(var)->next;
251  else
252  D_RW(prev)->next = D_RO(var)->next;
253  D_RW(hashmap)->count--;
254  TX_FREE(var);
255  } TX_ONABORT {
256  fprintf(stderr, "remove transaction aborted: %s\n",
257  pmemobj_errormsg());
258  ret = -1;
259  } TX_END
260 
261  if (ret)
262  return MEMOID_NULL;
263 
264  if (D_RO(hashmap)->count < D_RO(buckets)->nbuckets)
265  hm_tx_rebuild(pop, hashmap, D_RO(buckets)->nbuckets / 2);
266 
267  return retoid;
268 }
269 
280 int
281 hm_tx_foreach(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap,
282  int (*cb)(uint64_t key, MEMoid value, void *arg), void *arg)
283 {
284  TOID(struct buckets) buckets = D_RO(hashmap)->buckets;
285  TOID(struct entry) var;
286 
287  int ret = 0;
288  for (size_t i = 0; i < D_RO(buckets)->nbuckets; ++i) {
289  if (TOID_IS_NULL(D_RO(buckets)->bucket[i]))
290  continue;
291 
292  for (var = D_RO(buckets)->bucket[i]; !TOID_IS_NULL(var);
293  var = D_RO(var)->next) {
294  ret = cb(D_RO(var)->key, D_RO(var)->value, arg);
295  if (ret)
296  break;
297  }
298  }
299 
300  return ret;
301 }
302 
313 void
314 hm_tx_debug(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap, FILE *out)
315 {
316  TOID(struct buckets) buckets = D_RO(hashmap)->buckets;
317  TOID(struct entry) var;
318 
319  fprintf(out, "a: %u b: %u p: %" PRIu64 "\n", D_RO(hashmap)->hash_fun_a,
320  D_RO(hashmap)->hash_fun_b, D_RO(hashmap)->hash_fun_p);
321  fprintf(out, "count: %" PRIu64 ", buckets: %zu\n",
322  D_RO(hashmap)->count, D_RO(buckets)->nbuckets);
323 
324  for (size_t i = 0; i < D_RO(buckets)->nbuckets; ++i) {
325  if (TOID_IS_NULL(D_RO(buckets)->bucket[i]))
326  continue;
327 
328  int num = 0;
329  fprintf(out, "%zu: ", i);
330  for (var = D_RO(buckets)->bucket[i]; !TOID_IS_NULL(var);
331  var = D_RO(var)->next) {
332  fprintf(out, "%" PRIu64 " ", D_RO(var)->key);
333  num++;
334  }
335  fprintf(out, "(%d)\n", num);
336  }
337 }
338 
348 MEMoid
349 hm_tx_get(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap, uint64_t key)
350 {
351  TOID(struct buckets) buckets = D_RO(hashmap)->buckets;
352  TOID(struct entry) var;
353 
354  uint64_t h = hash(&hashmap, &buckets, key);
355 
356  for (var = D_RO(buckets)->bucket[h];
357  !TOID_IS_NULL(var);
358  var = D_RO(var)->next)
359  if (D_RO(var)->key == key)
360  return D_RO(var)->value;
361 
362  return MEMOID_NULL;
363 }
364 
376 int
377 hm_tx_lookup(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap, uint64_t key)
378 {
379  TOID(struct buckets) buckets = D_RO(hashmap)->buckets;
380  TOID(struct entry) var;
381 
382  uint64_t h = hash(&hashmap, &buckets, key);
383 
384  for (var = D_RO(buckets)->bucket[h];
385  !TOID_IS_NULL(var);
386  var = D_RO(var)->next)
387  if (D_RO(var)->key == key)
388  return 1;
389 
390  return 0;
391 }
392 
400 size_t
401 hm_tx_count(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap)
402 {
403  return D_RO(hashmap)->count;
404 }
405 
414 int
415 hm_tx_init(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap)
416 {
417  srand(D_RO(hashmap)->seed);
418  return 0;
419 }
420 
432 int
433 hm_tx_create(PMEMobjpool *pop, TOID(struct hashmap_tx) *map, void *arg)
434 {
435  struct hashmap_args *args = (struct hashmap_args *)arg;
436  int ret = 0;
437  TX_BEGIN(pop) {
438  #ifdef DEBUG
439  printf("map address %p\n", (*map).oid.off);
440  #endif
441  //TX_ADD_DIRECT(map);
442  //*map = TX_ZNEW(struct hashmap_tx);
443  uint32_t seed = args ? args->seed : 0;
444  create_hashmap(pop, *map, seed);
445  } TX_ONABORT {
446  printf("hm create errno = %d\n", errno);
447  ret = -1;
448  } TX_END
449 
450  return ret;
451 }
452 
462 int
463 hm_tx_check(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap)
464 {
465  return TOID_IS_NULL(hashmap) || !TOID_VALID(hashmap);
466 }
467 
479 int
480 hm_tx_cmd(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap,
481  unsigned cmd, uint64_t arg)
482 {
483  switch (cmd) {
484  case HASHMAP_CMD_REBUILD:
485  hm_tx_rebuild(pop, hashmap, arg);
486  return 0;
487  case HASHMAP_CMD_DEBUG:
488  if (!arg)
489  return -EINVAL;
490  hm_tx_debug(pop, hashmap, (FILE *)arg);
491  return 0;
492  default:
493  return -EINVAL;
494  }
495 }
hashmap_tx
Definition: hashmap_tx.h:48
hashmap_args
Definition: hashmap_tx.h:9
HASH_FUNC_COEFF_P
#define HASH_FUNC_COEFF_P
Definition: hashmap_tx.c:14
pop
PMEMobjpool * pop
Definition: types.c:7
MEMoid_st
The struct that stores the memptr for the object.
Definition: malloc.h:29
MAX_HASHSET_THRESHOLD
#define MAX_HASHSET_THRESHOLD
Definition: hashmap_tx.c:23
hm_tx_remove
MEMoid hm_tx_remove(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap, uint64_t key)
Removes specified value from the hashmap.
Definition: hashmap_tx.c:224
hm_tx_cmd
int hm_tx_cmd(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap, unsigned cmd, uint64_t arg)
Executes a command for hashmap.
Definition: hashmap_tx.c:480
hm_tx_create
int hm_tx_create(PMEMobjpool *pop, TOID(struct hashmap_tx) *map, void *arg)
Allocates a new hashmap.
Definition: hashmap_tx.c:433
uint64_t
__uint64_t uint64_t
Definition: globals.h:51
HASHMAP_CMD_REBUILD
@ HASHMAP_CMD_REBUILD
Definition: hashmap_tx.h:14
hm_tx_rebuild
static void hm_tx_rebuild(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap, size_t new_len)
Rebuilds the hashmap when the hashmap size threshold is reached.
Definition: hashmap_tx.c:102
create_hashmap
static void create_hashmap(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap, uint32_t seed)
Hashmap initializer.
Definition: hashmap_tx.c:38
hm_tx_count
size_t hm_tx_count(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap)
Returns number of elements in the hashmap.
Definition: hashmap_tx.c:401
hash
static uint64_t hash(const TOID(struct hashmap_tx) *hashmap, const TOID(struct buckets) *buckets, uint64_t value)
The hash function.
Definition: hashmap_tx.c:76
buckets::nbuckets
size_t nbuckets
Definition: hashmap_tx.h:43
INIT_BUCKETS_NUM
#define INIT_BUCKETS_NUM
Definition: hashmap_tx.c:17
TOID
TOID(struct hashmap_tx)
Definition: types.c:11
hm_tx_foreach
int hm_tx_foreach(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap, int(*cb)(uint64_t key, MEMoid value, void *arg), void *arg)
Calls a given callback for every element in the hashmap.
Definition: hashmap_tx.c:281
HASHMAP_CMD_DEBUG
@ HASHMAP_CMD_DEBUG
Definition: hashmap_tx.h:15
hm_tx_init
int hm_tx_init(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap)
Recovers hashmap state.
Definition: hashmap_tx.c:415
MEMOID_NULL
static const MEMoid MEMOID_NULL
Just a dummy obj.
Definition: malloc.h:52
hm_tx_lookup
int hm_tx_lookup(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap, uint64_t key)
Checks whether specified key exists in the hashmap.
Definition: hashmap_tx.c:377
hm_tx_check
int hm_tx_check(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap)
Checks if specified persistent object is an instance of hashmap.
Definition: hashmap_tx.c:463
MIN_HASHSET_THRESHOLD
#define MIN_HASHSET_THRESHOLD
Definition: hashmap_tx.c:20
entry
Definition: hashmap_tx.h:33
hm_tx_insert
int hm_tx_insert(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap, uint64_t key, MEMoid value)
Inserts the specified value into the hashmap.
Definition: hashmap_tx.c:164
hashmap_args::seed
uint32_t seed
Definition: hashmap_tx.h:10
buckets
Definition: hashmap_tx.h:41
uint32_t
__uint32_t uint32_t
Definition: globals.h:52
hm_tx_debug
void hm_tx_debug(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap, FILE *out)
Prints complete hashmap state.
Definition: hashmap_tx.c:314
hm_tx_get
MEMoid hm_tx_get(PMEMobjpool *pop, TOID(struct hashmap_tx) hashmap, uint64_t key)
Checks whether specified key is in the hashmap and retries the value associated with it.
Definition: hashmap_tx.c:349
hashmap_tx.h