Skip to content

Commit

Permalink
distance>0 insert & fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
dpdani committed Nov 15, 2023
1 parent 6183d8d commit 410d233
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 86 deletions.
50 changes: 42 additions & 8 deletions src/cereggii/atomic_dict/atomic_dict.c
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@ AtomicDict_GetItem(AtomicDict *self, PyObject *key)

if (result.entry_p == NULL) {
PyErr_SetObject(PyExc_KeyError, key);
result.entry.value = NULL;
}

Py_DECREF(meta);
Expand Down Expand Up @@ -591,7 +592,13 @@ atomic_dict_get_empty_entry(AtomicDict *dk, atomic_dict_meta *meta, atomic_dict_
entry_loc->entry = NULL;
}

void
typedef enum atomic_dict_robin_hood_result {
ok,
failed,
grow,
} atomic_dict_robin_hood_result;

atomic_dict_robin_hood_result
atomic_dict_robin_hood(atomic_dict_meta *meta, atomic_dict_node *nodes, atomic_dict_node to_insert, int distance_0_ix)
{
/*
Expand All @@ -607,19 +614,22 @@ atomic_dict_robin_hood(atomic_dict_meta *meta, atomic_dict_node *nodes, atomic_d
* 2. there is at least 1 empty node
* */

int nodes_in_two_words = 16 / (meta->node_size / 8);
atomic_dict_node current = to_insert;
atomic_dict_node temp;
int probe = 0;
int reservations = 0;
int cursor;

beginning:
for (; probe < nodes_in_two_words; probe++) {
int cursor = distance_0_ix + probe + reservations;
for (; probe < meta->nodes_in_two_regions; probe++) {
if (probe >= (1 << meta->distance_size) - 1) {
return grow;
}
cursor = distance_0_ix + probe + reservations;
if (nodes[cursor].node == 0) {
current.distance = probe;
nodes[cursor] = current;
return;
return ok;
}

if (atomic_dict_node_is_reservation(&nodes[cursor], meta)) {
Expand All @@ -635,6 +645,7 @@ atomic_dict_robin_hood(atomic_dict_meta *meta, atomic_dict_node *nodes, atomic_d

if (nodes[cursor].distance < probe) {
current.distance = probe;
atomic_dict_compute_raw_node(&current, meta);
temp = nodes[cursor];
nodes[cursor] = current;
current = temp;
Expand All @@ -643,6 +654,7 @@ atomic_dict_robin_hood(atomic_dict_meta *meta, atomic_dict_node *nodes, atomic_d
goto beginning;
}
}
return failed;
}

void
Expand Down Expand Up @@ -712,10 +724,32 @@ atomic_dict_insert_entry(atomic_dict_meta *meta, atomic_dict_entry_loc *entry_lo

for (int i = start; i < meta->nodes_in_two_regions; ++i) {
if (read_buffer[i].node == 0) {
node.distance = i;
atomic_dict_nodes_copy_buffers(read_buffer, temp);
atomic_dict_robin_hood(meta, temp, node, start);
if (atomic_dict_atomic_write_nodes_at(ix, i + 1 - start, &read_buffer[start], &temp[start], meta)) {
atomic_dict_robin_hood_result rh = atomic_dict_robin_hood(meta, temp, node, start);
// if (rh == grow) {
// atomic_dict_grow();
// }
assert(rh == ok);
int begin_write = -1;
for (int j = start; j < meta->nodes_in_two_regions; ++j) {
atomic_dict_compute_raw_node(&temp[j], meta);
if (temp[j].node != read_buffer[j].node) {
begin_write = j;
break;
}
}
assert(begin_write != -1);
int end_write;
for (int j = begin_write + 1; j < meta->nodes_in_two_regions; ++j) {
atomic_dict_compute_raw_node(&temp[j], meta);
if (temp[j].node == read_buffer[j].node) {
end_write = j;
break;
}
}
assert(end_write > begin_write);
if (atomic_dict_atomic_write_nodes_at(ix + begin_write - start, end_write - begin_write,
&read_buffer[begin_write], &temp[begin_write], meta)) {
goto done;
}
goto beginning;
Expand Down
81 changes: 9 additions & 72 deletions src/cereggii/atomic_dict/atomic_dict_node_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ atomic_dict_read_2_nodes_at(unsigned long ix, atomic_dict_node *nodes, atomic_di
node_region_big = meta->index[big];
}
atomic_dict_parse_node_from_region(ix, node_region_little, &nodes[0], meta);
atomic_dict_parse_node_from_region(ix, node_region_big, &nodes[1], meta);
atomic_dict_parse_node_from_region(ix + 1, node_region_big, &nodes[1], meta);
}

void
Expand All @@ -88,7 +88,7 @@ atomic_dict_read_4_nodes_at(unsigned long ix, atomic_dict_node *nodes, atomic_di
unsigned long region;
for (int i = 0; i < 4; ++i) {
region = ((ix + i) & ~meta->shift_mask) == little ? node_region_little : node_region_big;
atomic_dict_parse_node_from_region(ix, region, &nodes[i], meta);
atomic_dict_parse_node_from_region(ix + i, region, &nodes[i], meta);
}
}

Expand Down Expand Up @@ -126,7 +126,7 @@ atomic_dict_read_16_nodes_at(unsigned long ix, atomic_dict_node *nodes, atomic_d
unsigned long region;
for (int i = 0; i < 16; ++i) {
region = ((ix + i) & ~meta->shift_mask) == little ? node_region_little : node_region_big;
atomic_dict_parse_node_from_region(ix, region, &nodes[i], meta);
atomic_dict_parse_node_from_region(ix + i, region, &nodes[i], meta);
}
}

Expand Down Expand Up @@ -175,83 +175,20 @@ atomic_dict_atomic_write_nodes_at(unsigned long ix, int n, atomic_dict_node *exp
assert(n <= 16);
assert(ix < 1 << meta->log_size);
unsigned long shift = ix & meta->shift_mask;
unsigned long little = ix / meta->nodes_in_region;
unsigned long big = (ix + n - 1) / meta->nodes_in_region % (1 << meta->log_size);
unsigned long little = ix & ~meta->shift_mask;
unsigned long big = (ix + n - 1) & ~meta->shift_mask % (1 << meta->log_size);
assert(little <= big <= little + 1); // XXX implement index circular behavior
int must_write_nodes = little == big ? meta->nodes_in_region : meta->nodes_in_two_regions;
unsigned long long expected_raw = 0, new_raw = 0;
int i;
for (i = 0; i < n; ++i) {
atomic_dict_compute_raw_node(&expected[i], meta);
atomic_dict_compute_raw_node(&new[i], meta);
}
for (i = 0; i < n; ++i) {
expected_raw |= expected[i].node << (meta->node_size * (n - i - 1));
new_raw |= new[i].node << (meta->node_size * (n - i - 1));
expected_raw |= expected[i].node << (meta->node_size * i);
new_raw |= new[i].node << (meta->node_size * i);
}
// int nodes_missing;
// switch (n_bytes) {
// case 1:
// assert(meta->node_size <= 8);
// nodes_missing = 0;
// break;
// case 2:
// assert(meta->node_size <= 16);
// nodes_missing = 0;
// break;
// case 3:
// assert(meta->node_size <= 8);
// nodes_missing = 1;
// break;
// case 4:
// assert(meta->node_size <= 32);
// nodes_missing = 0;
// break;
// case 5:
// assert(meta->node_size == 8);
// nodes_missing = 1;
// break;
// case 6:
// assert(meta->node_size <= 16);
// nodes_missing = 2 / (meta->node_size / 8);
// break;
// case 7:
// assert(meta->node_size <= 8);
// nodes_missing = 3;
// break;
// case 8:
// assert(meta->node_size <= 64);
// nodes_missing = 0;
// break;
// case 9:
// assert(meta->node_size <= 8);
// nodes_missing = 7;
// break;
// case 10:
// assert(meta->node_size <= 16);
// nodes_missing = 8 / (meta->node_size / 8);
// break;
// case 11:
// assert(meta->node_size <= 8);
// break;
// case 12:
// assert(meta->node_size <= 32);
// break;
// case 13:
// assert(meta->node_size <= 8);
// break;
// case 14:
// assert(meta->node_size <= 16);
// break;
// case 15:
// assert(meta->node_size <= 8);
// break;
// case 16:
// assert(meta->node_size <= 64);
// break;
// default:
// return 0;
// }

int n_bytes = (meta->node_size >> 3) * n;
assert(n_bytes <= 16);
int must_write;
Expand All @@ -268,7 +205,7 @@ atomic_dict_atomic_write_nodes_at(unsigned long ix, int n, atomic_dict_node *exp
} else {
assert(0);
}
must_write_nodes = must_write / (meta->node_size / 8);
int must_write_nodes = must_write / (meta->node_size / 8);
for (; i < must_write_nodes; ++i) {
new_raw |= expected[i].node << (meta->node_size * (meta->node_size - i - 1));
}
Expand Down
29 changes: 23 additions & 6 deletions tests/test_atomic_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ def test_log_size_bumped():
def test_key_error():
d = AtomicDict()
with raises(KeyError):
# noinspection PyStatementEffect
d[0]
d[0] # noqa


def test_getitem():
Expand All @@ -51,6 +50,15 @@ def test_getitem():
assert c["spam"] == 43


def test_getitem_confused():
d = AtomicDict()
d[0] = 1
d[64] = 2
d[128] = 3
with raises(KeyError):
d[256] # noqa


def test_setitem_updates_a_value_set_at_init():
d = AtomicDict({0: 1})
d[0] = 3
Expand All @@ -61,8 +69,10 @@ def test_setitem_inserts_a_value():
d = AtomicDict(initial_size=64 * 4)
d[0] = 42
d[2] = 2
d[128] = 1
assert d[0] == 42
assert d[2] == 2
assert d[128] == 1


def test_setitem_updates_an_inserted_value():
Expand All @@ -73,10 +83,17 @@ def test_setitem_updates_an_inserted_value():
assert d[0] == 2


# def test_setitem_distance_1_insert():
# d = AtomicDict({0: 1})
# d[64] = 42
# assert d[64] == 42
def test_setitem_distance_1_insert():
d = AtomicDict({0: 1})
d[64] = 42
assert d[64] == 42
assert d.debug()['index'][1] == 18
d = AtomicDict()
d[0] = 1
d[1] = 2
d[64] = 3
assert d[64] == 3
assert d.debug()['index'][1] == 10


def test_dealloc():
Expand Down

0 comments on commit 410d233

Please sign in to comment.