From 0a8eadcb018cea27edd54af5d460e0e81e6d38a2 Mon Sep 17 00:00:00 2001 From: Conrad Zimmerman Date: Sun, 14 Jul 2024 23:54:29 -0700 Subject: [PATCH] Fix compaction --- src/main/resources/runtime.c0 | 54 ++++++++++++++--------------- src/test/resources/c0/quickcheck.c0 | 42 +++++++++++++++++----- src/test/resources/c0/test.c0 | 5 +-- 3 files changed, 60 insertions(+), 41 deletions(-) diff --git a/src/main/resources/runtime.c0 b/src/main/resources/runtime.c0 index 80c3964..c40ea5c 100644 --- a/src/main/resources/runtime.c0 +++ b/src/main/resources/runtime.c0 @@ -25,7 +25,7 @@ int runtime_compact(FieldArray*[] contents, int capacity, int newId) { start++; int removed = 0; - int insert = -1; + int insert = start; int i = start + 1; while (i != start) { FieldArray* entry = contents[i]; @@ -33,45 +33,42 @@ int runtime_compact(FieldArray*[] contents, int capacity, int newId) { // skip } else if (entry->accessible == 0 && entry->id != newId) { removed++; - if (insert == -1) - insert = i; - } else if (insert != -1) { - int psl = entry->psl; - if (psl == 0) { - // NULL all locations from `insert` up to `i` - while (insert != i) { - contents[insert] = NULL; - insert = (insert + 1) % capacity; - } - - insert = -1; - } else { + } else { + if (insert != i) { + int psl = entry->psl; // Calculate the distance between `insert` and `i` // Allow `i` to wrap around before `insert` does - int distance = insert < i ? insert - i : capacity + i - insert; + int distance = insert <= i ? i - insert : capacity + i - insert; if (psl < distance) { // Only move up to `psl` spots backwards, otherwise it would be prior // to the hash location - insert = (insert + distance - psl) % capacity; + int newInsert = (insert + distance - psl) % capacity; + while (insert != newInsert) { + contents[insert] = NULL; + insert = (insert + 1) % capacity; + } + distance = psl; } - contents[insert] = entry; - insert = (insert + 1) % capacity; - entry->psl -= distance; + if (distance != 0) { + contents[insert] = entry; + entry->psl -= distance; + } + } + + insert = (insert + 1) % capacity; } i = (i + 1) % capacity; } // `i == start` - if (insert != -1) { - // NULL all locations from `insert` up to `start` (which is already NULL) - while (insert != i) { - contents[insert] = NULL; - insert = (insert + 1) % capacity; - } + // NULL all locations from `insert` up to `start` (which is already NULL) + while (insert != i) { + contents[insert] = NULL; + insert = (insert + 1) % capacity; } return removed; @@ -95,10 +92,11 @@ void runtime_grow(OwnedFields* fields, int newId) // Check if we can gain enough space just by dropping unused entries int unused = runtime_compact(contents, capacity, newId); - if (unused != 0) + if (unused != 0) { fields->length -= unused; - if (unused >= GROW_CONST) - return; + if (unused >= GROW_CONST) + return; + } // Otherwise, use a larger array and rehash int newCapacity = capacity + GROW_CONST; diff --git a/src/test/resources/c0/quickcheck.c0 b/src/test/resources/c0/quickcheck.c0 index 64f4dc3..08504eb 100644 --- a/src/test/resources/c0/quickcheck.c0 +++ b/src/test/resources/c0/quickcheck.c0 @@ -155,13 +155,27 @@ void check_fields(OwnedFields* fields) { assert(count == fields->length); // (2) } +bool removeNext(int id, List* tags, OwnedFields* fields) { + int length = tags->length; + int[] values = tags->values; + for (int j = 0; j < length; j++) { + int t = values[j]; + if (tag_id(t) == id) { + assert(runtime_tryRemove(fields, id, tag_field(t))); + list_remove(tags, t); + return true; + } + } + return false; +} + int main() { int INITIAL_SIZE = 2048; int MAX_ID = 1024; - int REPS = 2500; + int REPS = 5000; bool PRINT = false; - rand_t r = init_rand(2); + rand_t r = init_rand(1); List* tags = list_new(); int length = 0; @@ -189,11 +203,11 @@ int main() { // Check that tags coincides with the entries // This takes a bit of time - for (int j = 0; j < tags->length; j++) { + /*for (int j = 0; j < tags->length; j++) { int tag = tags->values[j]; runtime_assert(fields, tag_id(tag), tag_field(tag), "Missing field"); } - /* + for (int j = 0; j < fields->length; j++) { FieldArray* entry = fields->contents[j]; if (entry != NULL) { @@ -211,8 +225,8 @@ int main() { int type = abs(rand(r)) % 100; int id = abs(rand(r)) % MAX_ID; int numFields = field_count(id); - if (type < 10) { - // 10% of the time: Add a struct + if (type < 15) { + // 15% of the time: Add a struct if (!contains_id(tags, id)) { if (PRINT) printf("addAll(%d, %d)\n", id, numFields); runtime_addAll(fields, id, numFields); @@ -232,8 +246,18 @@ int main() { // if (PRINT) printf("Skipped: addAll(%d, %d)\n", id, numFields); i--; // Don't count this iteration } - } else if (type < 40) { - // 30% of the time: Add a single field + } else if (type < 30) { + // 15% of the time: Remove all fields for a single ID + if (contains_id(tags, id)) { + if (PRINT) printf("removeAll(%d, %d)\n", id, numFields); + while (removeNext(id, tags, fields)) + true; + } else { + // if (PRINT) printf("Skipped: removeAll(%d)\n", id); + i--; + } + } else if (type < 70) { + // 40% of the time: Add a single field int field = abs(rand(r)) % numFields; int t = tag(id, field); bool contained = list_contains(tags, t); @@ -258,7 +282,7 @@ int main() { assert(false); } } else { - // 60% of the time: Remove a single field + // 30% of the time: Remove a single field int field = abs(rand(r)) % numFields; int t = tag(id, field); bool contained = list_contains(tags, t); diff --git a/src/test/resources/c0/test.c0 b/src/test/resources/c0/test.c0 index 2729ecf..f1b3773 100644 --- a/src/test/resources/c0/test.c0 +++ b/src/test/resources/c0/test.c0 @@ -89,7 +89,7 @@ void test_remove() { } void test_resizing() { - println("test_resizing: "); + print("test_resizing: "); OwnedFields* fields = runtime_init(); @@ -152,9 +152,7 @@ void test_resizingRemove() { assert(fields->capacity == 1280); // Verify that we still have access to IDs 1000-1022 and 2000-2999 - runtime_print(fields); for (int i = 1000; i < 1023; i++) { - printf("i: %d\n", i); assert(checkAcc(fields, i, 0)); assert(checkAcc(fields, i, 1)); } @@ -301,6 +299,5 @@ int main() { test_ensureSeparate(); println("All runtime tests passed"); - printint(-5 % 10); return 0; }