Skip to content

Commit c948a44

Browse files
author
icgmilk
committed
Switch hashmap implementation to open addressing
Replace chaining with open addressing using Linear Probing. Set load factor to 50% to balance probe length and performance. This change trades ~320 kB of memory for ~30 ms faster execution, primarily due to better cache locality.
1 parent f265042 commit c948a44

File tree

2 files changed

+58
-75
lines changed

2 files changed

+58
-75
lines changed

src/defs.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,13 @@ typedef struct {
100100
typedef struct hashmap_node {
101101
char *key;
102102
void *val;
103-
struct hashmap_node *next;
103+
bool state; /* true: occupied, false: empty */
104104
} hashmap_node_t;
105105

106106
typedef struct {
107107
int size;
108108
int cap;
109-
hashmap_node_t **buckets;
109+
hashmap_node_t *table;
110110
} hashmap_t;
111111

112112
/* lexer tokens */

src/globals.c

Lines changed: 56 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -414,90 +414,61 @@ hashmap_t *hashmap_create(int cap)
414414

415415
map->size = 0;
416416
map->cap = round_up_pow2(cap);
417-
map->buckets = calloc(map->cap, sizeof(hashmap_node_t *));
417+
map->table = calloc(map->cap, sizeof(hashmap_node_t));
418418

419-
if (!map->buckets) {
420-
printf("Failed to allocate buckets in hashmap_t\n");
419+
if (!map->table) {
420+
printf("Failed to allocate table in hashmap_t\n");
421421
free(map);
422422
return NULL;
423423
}
424424

425425
return map;
426426
}
427427

428-
/* Create a hashmap node on heap.
429-
* @key: The key of node. Must not be NULL.
430-
* @val: The value of node. Could be NULL.
431-
*
432-
* Return: The pointer of created node.
433-
*/
434-
hashmap_node_t *hashmap_node_new(char *key, void *val)
435-
{
436-
if (!key)
437-
return NULL;
438-
439-
const int len = strlen(key);
440-
hashmap_node_t *node = arena_alloc(HASHMAP_ARENA, sizeof(hashmap_node_t));
441-
442-
443-
if (!node) {
444-
printf("Failed to allocate hashmap_node_t\n");
445-
return NULL;
446-
}
447-
448-
node->key = arena_alloc(HASHMAP_ARENA, len + 1);
449-
if (!node->key) {
450-
printf("Failed to allocate hashmap_node_t key with size %d\n", len + 1);
451-
return NULL;
452-
}
453-
454-
strcpy(node->key, key);
455-
node->val = val;
456-
node->next = NULL;
457-
return node;
458-
}
459428

460429
void hashmap_rehash(hashmap_t *map)
461430
{
462431
if (!map)
463432
return;
464433

465434
int old_cap = map->cap;
466-
hashmap_node_t **old_buckets = map->buckets;
435+
hashmap_node_t *old_table = map->table;
467436

468437
map->cap <<= 1;
469-
map->buckets = calloc(map->cap, sizeof(hashmap_node_t *));
438+
map->table = calloc(map->cap, sizeof(hashmap_node_t));
470439

471-
if (!map->buckets) {
472-
printf("Failed to allocate new buckets in hashmap_t\n");
473-
map->buckets = old_buckets;
440+
if (!map->table) {
441+
printf("Failed to allocate new table in hashmap_t\n");
442+
map->table = old_table;
474443
map->cap = old_cap;
475444
return;
476445
}
477446

447+
map->size = 0;
448+
478449
for (int i = 0; i < old_cap; i++) {
479-
hashmap_node_t *cur = old_buckets[i];
480-
hashmap_node_t *next;
481-
hashmap_node_t *target_cur;
482-
483-
while (cur) {
484-
next = cur->next;
485-
cur->next = NULL;
486-
int index = hashmap_hash_index(map->cap, cur->key);
487-
target_cur = map->buckets[index];
488-
489-
if (!target_cur) {
490-
map->buckets[index] = cur;
491-
} else {
492-
cur->next = target_cur;
493-
map->buckets[index] = cur;
450+
if (old_table[i].state == true) {
451+
char *key = old_table[i].key;
452+
void *val = old_table[i].val;
453+
454+
int index = hashmap_hash_index(map->cap, key);
455+
int start = index;
456+
457+
while (map->table[index].state == true) {
458+
index = (index + 1) & (map->cap - 1);
459+
if (index == start) {
460+
printf("Error: New table is full during rehash\n");
461+
abort();
462+
}
494463
}
495464

496-
cur = next;
465+
map->table[index].key = key;
466+
map->table[index].val = val;
467+
map->table[index].state = true;
468+
map->size++;
497469
}
498470
}
499-
500-
free(old_buckets);
471+
free(old_table);
501472
}
502473

503474
/* Put a key-value pair into given hashmap.
@@ -513,22 +484,28 @@ void hashmap_put(hashmap_t *map, char *key, void *val)
513484
if (!map)
514485
return;
515486

487+
/* Check if size of map exceeds load factor 50% (or 1/2 of capacity) */
488+
if ((map->cap >> 1) <= map->size)
489+
hashmap_rehash(map);
490+
516491
int index = hashmap_hash_index(map->cap, key);
517-
hashmap_node_t *cur = map->buckets[index],
518-
*new_node = hashmap_node_new(key, val);
492+
int start = index;
519493

520-
if (!cur) {
521-
map->buckets[index] = new_node;
522-
} else {
523-
while (cur->next)
524-
cur = cur->next;
525-
cur->next = new_node;
494+
while (map->table[index].state == true) {
495+
if (!strcmp(map->table[index].key, key)) {
496+
map->table[index].val = val;
497+
return;
498+
}
499+
500+
index = (index + 1) & (map->cap - 1);
501+
if (index == start)
502+
return;
526503
}
527504

505+
map->table[index].key = arena_strdup(HASHMAP_ARENA, key);
506+
map->table[index].val = val;
507+
map->table[index].state = true;
528508
map->size++;
529-
/* Check if size of map exceeds load factor 75% (or 3/4 of capacity) */
530-
if ((map->cap >> 2) + (map->cap >> 1) <= map->size)
531-
hashmap_rehash(map);
532509
}
533510

534511
/* Get key-value pair node from hashmap from given key.
@@ -544,10 +521,16 @@ hashmap_node_t *hashmap_get_node(hashmap_t *map, char *key)
544521
return NULL;
545522

546523
int index = hashmap_hash_index(map->cap, key);
524+
int start = index;
547525

548-
for (hashmap_node_t *cur = map->buckets[index]; cur; cur = cur->next)
549-
if (!strcmp(cur->key, key))
550-
return cur;
526+
while (map->table[index].state == true) {
527+
if (!strcmp(map->table[index].key, key))
528+
return &map->table[index];
529+
530+
index = (index + 1) & (map->cap - 1);
531+
if (index == start)
532+
return NULL;
533+
}
551534

552535
return NULL;
553536
}
@@ -574,7 +557,7 @@ void *hashmap_get(hashmap_t *map, char *key)
574557
*/
575558
bool hashmap_contains(hashmap_t *map, char *key)
576559
{
577-
return hashmap_get_node(map, key);
560+
return hashmap_get_node(map, key) != NULL;
578561
}
579562

580563
/* Free the hashmap, this also frees key-value pair entry's value.
@@ -585,7 +568,7 @@ void hashmap_free(hashmap_t *map)
585568
if (!map)
586569
return;
587570

588-
free(map->buckets);
571+
free(map->table);
589572
free(map);
590573
}
591574

0 commit comments

Comments
 (0)