diff --git a/.gitignore b/.gitignore index eb5a7e9..7adaeca 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ **/*.o tests/test examples/ex_? +examples/soak .cache hints.txt tmp/ diff --git a/Makefile b/Makefile index db623e9..076beec 100644 --- a/Makefile +++ b/Makefile @@ -5,16 +5,16 @@ SHARED_LIB = libsparsemap.so #CFLAGS = -Wall -Wextra -Wpedantic -Of -std=c11 -Iinclude/ -fPIC #CFLAGS = -Wall -Wextra -Wpedantic -Og -g -std=c11 -Iinclude/ -fPIC -CFLAGS = -DSPARSEMAP_DIAGNOSTIC -DDEBUG -Wall -Wextra -Wpedantic -Og -g -std=c11 -Iinclude/ -fPIC +CFLAGS = -DSPARSEMAP_DIAGNOSTIC -DDEBUG -Wall -Wextra -Wpedantic -O0 -g -std=c11 -Iinclude/ -fPIC #CFLAGS = -DSPARSEMAP_DIAGNOSTIC -DDEBUG -Wall -Wextra -Wpedantic -Og -g -fsanitize=address,leak,object-size,pointer-compare,pointer-subtract,null,return,bounds,pointer-overflow,undefined -fsanitize-address-use-after-scope -std=c11 -Iinclude/ -fPIC #CFLAGS = -Wall -Wextra -Wpedantic -Og -g -fsanitize=all -fhardened -std=c11 -Iinclude/ -fPIC -TEST_FLAGS = -DDEBUG -Wall -Wextra -Wpedantic -Og -g -std=c11 -Iinclude/ -Itests/ -fPIC +TEST_FLAGS = -DDEBUG -Wall -Wextra -Wpedantic -O0 -g -std=c11 -Iinclude/ -Itests/ -fPIC #TEST_FLAGS = -DDEBUG -Wall -Wextra -Wpedantic -Og -g -fsanitize=address,leak,object-size,pointer-compare,pointer-subtract,null,return,bounds,pointer-overflow,undefined -fsanitize-address-use-after-scope -std=c11 -Iinclude/ -fPIC TESTS = tests/test TEST_OBJS = tests/test.o tests/munit.o tests/tdigest.o tests/common.o -EXAMPLES = examples/ex_1 examples/ex_2 examples/ex_3 examples/ex_4 +EXAMPLES = examples/ex_1 examples/ex_2 examples/ex_3 examples/ex_4 examples/soak .PHONY: all shared static clean test examples mls @@ -50,7 +50,7 @@ clean: rm -f $(EXAMPLES) examples/*.o format: - clang-format -i src/sparsemap.c include/sparsemap.h examples/ex_*.c tests/test.c tests/common.c tests/common.h + clang-format -i src/sparsemap.c include/sparsemap.h examples/ex_*.c examples/soak.c tests/test.c tests/common.c tests/common.h # clang-format -i include/*.h src/*.c tests/*.c tests/*.h examples/*.c %.o: src/%.c @@ -77,7 +77,11 @@ examples/ex_3: examples/common.o examples/ex_3.o $(STATIC_LIB) examples/ex_4: examples/common.o examples/ex_4.o $(STATIC_LIB) $(CC) $^ -o $@ $(CFLAGS) $(TEST_FLAGS) +examples/soak: examples/common.o examples/soak.o $(STATIC_LIB) + $(CC) $^ -o $@ $(CFLAGS) $(TEST_FLAGS) + todo: rg -i 'todo|gsb|abort' # cp src/sparsemap.c /tmp && clang-tidy src/sparsemap.c -fix -fix-errors -checks="readability-braces-around-statements" -- -DDEBUG -DSPARSEMAP_DIAGNOSTIC -DSPARSEMAP_ASSERT -Wall -Wextra -Wpedantic -Og -g -std=c11 -Iinclude/ -fPIC +# clear; make clean examples test && env ASAN_OPTIONS=detect_leaks=1 LSAN_OPTIONS=verbosity=1:log_threads=1 ./tests/test diff --git a/examples/soak.c b/examples/soak.c new file mode 100644 index 0000000..8c95d8d --- /dev/null +++ b/examples/soak.c @@ -0,0 +1,648 @@ +#include +#include +#include +#include +#include +#include + +#include "../include/sparsemap.h" +#include "../tests/common.h" + +/* midl.h ------------------------------------------------------------------ */ +/** @defgroup idls ID List Management + * @{ + */ +/** A generic unsigned ID number. These were entryIDs in back-bdb. + * Preferably it should have the same size as a pointer. + */ +typedef size_t MDB_ID; + +/** An IDL is an ID List, a sorted array of IDs. The first + * element of the array is a counter for how many actual + * IDs are in the list. In the original back-bdb code, IDLs are + * sorted in ascending order. For libmdb IDLs are sorted in + * descending order. + */ +typedef MDB_ID *MDB_IDL; + +/* IDL sizes - likely should be even bigger + * limiting factors: sizeof(ID), thread stack size + */ +#define MDB_IDL_LOGN 16 /* DB_SIZE is 2^16, UM_SIZE is 2^17 */ +#define MDB_IDL_DB_SIZE (1 << MDB_IDL_LOGN) +#define MDB_IDL_UM_SIZE (1 << (MDB_IDL_LOGN + 1)) + +#define MDB_IDL_DB_MAX (MDB_IDL_DB_SIZE - 1) +#define MDB_IDL_UM_MAX (MDB_IDL_UM_SIZE - 1) + +#define MDB_IDL_SIZEOF(ids) (((ids)[0] + 1) * sizeof(MDB_ID)) +#define MDB_IDL_IS_ZERO(ids) ((ids)[0] == 0) +#define MDB_IDL_CPY(dst, src) (memcpy(dst, src, MDB_IDL_SIZEOF(src))) +#define MDB_IDL_FIRST(ids) ((ids)[1]) +#define MDB_IDL_LAST(ids) ((ids)[(ids)[0]]) + +/** Current max length of an #mdb_midl_alloc()ed IDL */ +#define MDB_IDL_ALLOCLEN(ids) ((ids)[-1]) + +/** Append ID to IDL. The IDL must be big enough. */ +#define mdb_midl_xappend(idl, id) \ + do { \ + MDB_ID *xidl = (idl), xlen = ++(xidl[0]); \ + xidl[xlen] = (id); \ + } while (0) + +/** Search for an ID in an IDL. + * @param[in] ids The IDL to search. + * @param[in] id The ID to search for. + * @return The index of the first ID greater than or equal to \b id. + */ +unsigned mdb_midl_search(MDB_IDL ids, MDB_ID id); + +/** Allocate an IDL. + * Allocates memory for an IDL of the given size. + * @return IDL on success, NULL on failure. + */ +MDB_IDL mdb_midl_alloc(int num); + +/** Free an IDL. + * @param[in] ids The IDL to free. + */ +void mdb_midl_free(MDB_IDL ids); + +/** Shrink an IDL. + * Return the IDL to the default size if it has grown larger. + * @param[in,out] idp Address of the IDL to shrink. + */ +void mdb_midl_shrink(MDB_IDL *idp); + +/** Make room for num additional elements in an IDL. + * @param[in,out] idp Address of the IDL. + * @param[in] num Number of elements to make room for. + * @return 0 on success, ENOMEM on failure. + */ +int mdb_midl_need(MDB_IDL *idp, unsigned num); + +/** Append an ID onto an IDL. + * @param[in,out] idp Address of the IDL to append to. + * @param[in] id The ID to append. + * @return 0 on success, ENOMEM if the IDL is too large. + */ +int mdb_midl_append(MDB_IDL *idp, MDB_ID id); + +/** Append an IDL onto an IDL. + * @param[in,out] idp Address of the IDL to append to. + * @param[in] app The IDL to append. + * @return 0 on success, ENOMEM if the IDL is too large. + */ +int mdb_midl_append_list(MDB_IDL *idp, MDB_IDL app); + +/** Append an ID range onto an IDL. + * @param[in,out] idp Address of the IDL to append to. + * @param[in] id The lowest ID to append. + * @param[in] n Number of IDs to append. + * @return 0 on success, ENOMEM if the IDL is too large. + */ +int mdb_midl_append_range(MDB_IDL *idp, MDB_ID id, unsigned n); + +/** Merge an IDL onto an IDL. The destination IDL must be big enough. + * @param[in] idl The IDL to merge into. + * @param[in] merge The IDL to merge. + */ +void mdb_midl_xmerge(MDB_IDL idl, MDB_IDL merge); + +/** Sort an IDL. + * @param[in,out] ids The IDL to sort. + */ +void mdb_midl_sort(MDB_IDL ids); + +/* midl.c ------------------------------------------------------------------ */ +/** @defgroup idls ID List Management + * @{ + */ +#define CMP(x, y) ((x) < (y) ? -1 : (x) > (y)) + +unsigned +mdb_midl_search(MDB_IDL ids, MDB_ID id) +{ + /* + * binary search of id in ids + * if found, returns position of id + * if not found, returns first position greater than id + */ + unsigned base = 0; + unsigned cursor = 1; + int val = 0; + unsigned n = ids[0]; + + while (0 < n) { + unsigned pivot = n >> 1; + cursor = base + pivot + 1; + val = CMP(ids[cursor], id); + + if (val < 0) { + n = pivot; + + } else if (val > 0) { + base = cursor; + n -= pivot + 1; + + } else { + return cursor; + } + } + + if (val > 0) { + ++cursor; + } + return cursor; +} + +#if 0 /* superseded by append/sort */ +int mdb_midl_insert( MDB_IDL ids, MDB_ID id ) +{ + unsigned x, i; + + x = mdb_midl_search( ids, id ); + assert( x > 0 ); + + if( x < 1 ) { + /* internal error */ + return -2; + } + + if ( x <= ids[0] && ids[x] == id ) { + /* duplicate */ + assert(0); + return -1; + } + + if ( ++ids[0] >= MDB_IDL_DB_MAX ) { + /* no room */ + --ids[0]; + return -2; + + } else { + /* insert id */ + for (i=ids[0]; i>x; i--) + ids[i] = ids[i-1]; + ids[x] = id; + } + + return 0; +} +#endif + +MDB_IDL +mdb_midl_alloc(int num) +{ + MDB_IDL ids = malloc((num + 2) * sizeof(MDB_ID)); + if (ids) { + *ids++ = num; + *ids = 0; + } + return ids; +} + +void +mdb_midl_free(MDB_IDL ids) +{ + if (ids) + free(ids - 1); +} + +void +mdb_midl_shrink(MDB_IDL *idp) +{ + MDB_IDL ids = *idp; + if (*(--ids) > MDB_IDL_UM_MAX && (ids = realloc(ids, (MDB_IDL_UM_MAX + 2) * sizeof(MDB_ID)))) { + *ids++ = MDB_IDL_UM_MAX; + *idp = ids; + } +} + +static int +mdb_midl_grow(MDB_IDL *idp, int num) +{ + MDB_IDL idn = *idp - 1; + /* grow it */ + idn = realloc(idn, (*idn + num + 2) * sizeof(MDB_ID)); + if (!idn) + return ENOMEM; + *idn++ += num; + *idp = idn; + return 0; +} + +int +mdb_midl_need(MDB_IDL *idp, unsigned num) +{ + MDB_IDL ids = *idp; + num += ids[0]; + if (num > ids[-1]) { + num = (num + num / 4 + (256 + 2)) & -256; + if (!(ids = realloc(ids - 1, num * sizeof(MDB_ID)))) + return ENOMEM; + *ids++ = num - 2; + *idp = ids; + } + return 0; +} + +int +mdb_midl_append(MDB_IDL *idp, MDB_ID id) +{ + MDB_IDL ids = *idp; + /* Too big? */ + if (ids[0] >= ids[-1]) { + if (mdb_midl_grow(idp, MDB_IDL_UM_MAX)) + return ENOMEM; + ids = *idp; + } + ids[0]++; + ids[ids[0]] = id; + return 0; +} + +int +mdb_midl_append_list(MDB_IDL *idp, MDB_IDL app) +{ + MDB_IDL ids = *idp; + /* Too big? */ + if (ids[0] + app[0] >= ids[-1]) { + if (mdb_midl_grow(idp, app[0])) + return ENOMEM; + ids = *idp; + } + memcpy(&ids[ids[0] + 1], &app[1], app[0] * sizeof(MDB_ID)); + ids[0] += app[0]; + return 0; +} + +int +mdb_midl_append_range(MDB_IDL *idp, MDB_ID id, unsigned n) +{ + MDB_ID *ids = *idp, len = ids[0]; + /* Too big? */ + if (len + n > ids[-1]) { + if (mdb_midl_grow(idp, n | MDB_IDL_UM_MAX)) + return ENOMEM; + ids = *idp; + } + ids[0] = len + n; + ids += len; + while (n) + ids[n--] = id++; + return 0; +} + +void +mdb_midl_xmerge(MDB_IDL idl, MDB_IDL merge) +{ + MDB_ID old_id, merge_id, i = merge[0], j = idl[0], k = i + j, total = k; + idl[0] = (MDB_ID)-1; /* delimiter for idl scan below */ + old_id = idl[j]; + while (i) { + merge_id = merge[i--]; + for (; old_id < merge_id; old_id = idl[--j]) + idl[k--] = old_id; + idl[k--] = merge_id; + } + idl[0] = total; +} + +/* Quicksort + Insertion sort for small arrays */ + +#define SMALL 8 +#define MIDL_SWAP(a, b) \ + { \ + itmp = (a); \ + (a) = (b); \ + (b) = itmp; \ + } + +void +mdb_midl_sort(MDB_IDL ids) +{ + /* Max possible depth of int-indexed tree * 2 items/level */ + int istack[sizeof(int) * CHAR_BIT * 2]; + int i, j, k, l, ir, jstack; + MDB_ID a, itmp; + + ir = (int)ids[0]; + l = 1; + jstack = 0; + for (;;) { + if (ir - l < SMALL) { /* Insertion sort */ + for (j = l + 1; j <= ir; j++) { + a = ids[j]; + for (i = j - 1; i >= 1; i--) { + if (ids[i] >= a) + break; + ids[i + 1] = ids[i]; + } + ids[i + 1] = a; + } + if (jstack == 0) + break; + ir = istack[jstack--]; + l = istack[jstack--]; + } else { + k = (l + ir) >> 1; /* Choose median of left, center, right */ + MIDL_SWAP(ids[k], ids[l + 1]); + if (ids[l] < ids[ir]) { + MIDL_SWAP(ids[l], ids[ir]); + } + if (ids[l + 1] < ids[ir]) { + MIDL_SWAP(ids[l + 1], ids[ir]); + } + if (ids[l] < ids[l + 1]) { + MIDL_SWAP(ids[l], ids[l + 1]); + } + i = l + 1; + j = ir; + a = ids[l + 1]; + for (;;) { + do + i++; + while (ids[i] > a); + do + j--; + while (ids[j] < a); + if (j < i) + break; + MIDL_SWAP(ids[i], ids[j]); + } + ids[l + 1] = ids[j]; + ids[j] = a; + jstack += 2; + if (ir - i + 1 >= j - l) { + istack[jstack] = ir; + istack[jstack - 1] = i; + ir = j - 1; + } else { + istack[jstack] = j - 1; + istack[jstack - 1] = l; + l = i; + } + } + } +} +/* ------------------------------------------------------------------------- */ + +typedef MDB_ID pgno_t; + +char * +bytes_as(double bytes, char *s, size_t size) +{ + const char *units[] = { "b", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB" }; + size_t i = 0; + + while (bytes >= 1024 && i < sizeof(units) / sizeof(units[0]) - 1) { + bytes /= 1024; + i++; + } + + snprintf(s, size, "%.2f %s", bytes, units[i]); + return s; +} + +/** + * A "coin toss" function that is critical to the proper operation of the + * Skiplist. For example, when `max = 6` this function returns 0 with + * probability 0.5, 1 with 0.25, 2 with 0.125, etc. until 6 with 0.5^7. + */ +static int +toss(size_t max) +{ + size_t level = 0; + double probability = 0.5; + + double random_value = (double)xorshift32() / RAND_MAX; + while (random_value < probability && level < max) { + level++; + probability *= 0.5; + } + return level; +} + +bool +verify_span_midl(MDB_IDL list, pgno_t pg, unsigned len) +{ + pgno_t f = 1; + if (pg + len > list[0]) + return false; + while (list[f] != pg && f <= list[0]) + f++; + if (len == 1) + return true; + for (pgno_t i = f; i < f + len; i++) { + if (list[i + 1] != list[i] + 1) + return false; + } + return true; +} + +bool +verify_empty_midl(MDB_IDL list, pgno_t pg, unsigned len) +{ + for (pgno_t i = pg; i < pg + len; i++) { + pgno_t f = 1; + while (list[f] != pg && f <= list[0]) + f++; + if (f != list[0]) + return false; + } + return true; +} + +bool +verify_span_sparsemap(sparsemap_t *map, pgno_t pg, unsigned len) +{ + for (pgno_t i = pg; i < pg + len; i++) { + if (sparsemap_is_set(map, i) != true) { + return false; + } + } + return true; +} + +bool +verify_empty_sparsemap(sparsemap_t *map, pgno_t pg, unsigned len) +{ + for (pgno_t i = pg; i < pg + len; i++) { + if (sparsemap_is_set(map, i) != false) { + return false; + } + } + return true; +} + +bool +verify_sm_eq_ml(sparsemap_t *map, MDB_IDL list) +{ + // ensure all items in the MDB_IDL are set to true in the map + for (unsigned j = 1; j <= list[0]; j++) { + if (sparsemap_is_set(map, list[j]) == false) + return false; + } + // ensure all items not in the MDB_IDL are set to false in the map + unsigned j = 1, last_pgno = list[list[0]]; + for (unsigned i = 0; i <= last_pgno; i++) { + if (list[j] > i) { + do { + if (sparsemap_is_set(map, i) == true) + return false; + } while (i++ < list[j]); + } else { + if (sparsemap_is_set(map, i) == false) + return false; + if (list[j] == i) + j++; + } + } + return true; +} + +void +print_sizes(sparsemap_t *map, MDB_IDL list) +{ + char m[1024], l[1024]; + __diag("idl: %s bytes\tsm: %s bytes\n", bytes_as(MDB_IDL_SIZEOF(list), m, 1024), bytes_as(sparsemap_get_capacity(map), l, 1024)); +} + +/* + * A "soak test" that tries to replicate behavior in LMDB for page allocation. + */ +int +main() +{ + size_t iterations = 0; + bool prefer_mdb_idl_location = true; // xorshift32() % 2; + + // disable buffering + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + + __diag("starting...\n"); + + size_t amt = 1024 * 2; // 1024*1024 * 2; + MDB_IDL list = mdb_midl_alloc(amt); + sparsemap_t *map = sparsemap(3 * 1024); + + // start with 2GiB of 4KiB free pages to track: + // - MDB_IDL requires one int for each free page + // - Sparsemap will compress the set bits using less memory + mdb_midl_need(&list, amt); + for (size_t pg = 0; pg < amt; pg++) { + mdb_midl_xappend(list, pg); // listed page ids are free + sparsemap_set(map, pg, true); // true means free in our bitmap + } + print_sizes(map, list); + assert(verify_sm_eq_ml(map, list)); + + while (1) { + unsigned mi; + pgno_t ml = 0, sl = 0; + + // get an amount [1, 16] of pages to find prefering smaller sizes + unsigned n = toss(15) + 1; + + // find a set of pages using the MDB_IDL + { + /* Seek a big enough contiguous page range. Prefer + * pages at the tail, just truncating the list. + */ + int retry = 1; + unsigned i; + pgno_t pgno = 0, *mop = list; + unsigned n2 = n, mop_len = mop[0]; + if (mop_len > n2) { + i = mop_len; + if (n2 == 1) { + pgno = mop[mop_len]; + goto search_done; + } + do { + pgno = mop[i]; + if (mop[i - n2] == pgno - n2) + goto search_done; + } while (--i > n2); + if (--retry < 0) + break; + } + search_done:; + assert(pgno != 0); + ml = pgno - (n2 > 1 ? n2 - 1 : 0); + mi = i; + } + assert(verify_span_midl(list, ml, n)); + assert(verify_span_sparsemap(map, ml, n)); + + // find a set of pages using the Sparsemap + { + pgno_t pgno = sparsemap_span(map, 0, n, true); + assert(SPARSEMAP_NOT_FOUND(pgno) == false); + sl = pgno; + } + assert(verify_span_midl(list, sl, n)); + assert(verify_span_sparsemap(map, sl, n)); + + // acquire the set of pages within the list + if (prefer_mdb_idl_location) { + unsigned j, num = n; + int i = mi; + pgno_t *mop = list; + unsigned mop_len = mop[0]; + + mop[0] = mop_len -= num; + /* Move any stragglers down */ + for (j = i - num; j < mop_len;) + mop[++j] = mop[++i]; + } else { + unsigned j, num = n; + int i = mdb_midl_search(list, sl) + num; + pgno_t *mop = list; + unsigned mop_len = mop[0]; + + mop[0] = mop_len -= num; + /* Move any stragglers down */ + for (j = i - num; j < mop_len;) + mop[++j] = mop[++i]; + } + + // acquire the set of pages within the sparsemap + if (prefer_mdb_idl_location) { + for (pgno_t i = ml; i < ml + n; i++) { + sparsemap_set(map, i, false); + } + } else { + for (pgno_t i = sl; i <= sl + n; i++) { + sparsemap_set(map, i, false); + } + } + + assert(verify_sm_eq_ml(map, list)); + + // Once we've used half of the free list, let's replenish it a bit. + if (list[0] < amt / 2) { + do { + size_t len = toss(15) + 1; + pgno_t l, s = (pgno_t)xorshift32() % amt - len; + do { + l = sparsemap_span(map, s--, len, false); + } while(SPARSEMAP_NOT_FOUND(l)); + assert(verify_empty_midl(list, l, len)); + assert(verify_empty_sparsemap(map, l, len)); + for (int i = l; i < l + len; i++) { + mdb_midl_xappend(list, i); + sparsemap_set(map, i, true); + } + assert(verify_span_midl(list, l, len)); + assert(verify_span_sparsemap(map, l, len)); + } while (list[0] > amt - 32); + } + iterations++; + } + + return 0; +} diff --git a/include/sparsemap.h b/include/sparsemap.h index 689980b..233e530 100644 --- a/include/sparsemap.h +++ b/include/sparsemap.h @@ -89,7 +89,7 @@ extern "C" { typedef struct sparsemap sparsemap_t; typedef long int sparsemap_idx_t; -#define SPARSEMAP_IDX_MAX ((1UL << (sizeof(long) * CHAR_BIT - 1)) - 1) +#define SPARSEMAP_IDX_MAX ((1UL << (sizeof(long int) * CHAR_BIT - 1)) - 1) #define SPARSEMAP_IDX_MIN (-(SPARSEMAP_IDX_MAX)-1) #define SPARSEMAP_NOT_FOUND(_x) ((_x) == SPARSEMAP_IDX_MAX || (_x) == SPARSEMAP_IDX_MIN) typedef uint32_t sm_idx_t; diff --git a/src/sparsemap.c b/src/sparsemap.c index c5d88c5..3c05a55 100644 --- a/src/sparsemap.c +++ b/src/sparsemap.c @@ -902,11 +902,11 @@ sparsemap_init(sparsemap_t *map, uint8_t *data, size_t size) } void -sparsemap_open(sparsemap_t *map, uint8_t *data, size_t data_size) +sparsemap_open(sparsemap_t *map, uint8_t *data, size_t size) { map->m_data = data; map->m_data_used = map->m_data_used > 0 ? map->m_data_used : 0; - map->m_capacity = data_size; + map->m_capacity = size; } /* @@ -1410,17 +1410,18 @@ sparsemap_rank(sparsemap_t *map, size_t x, size_t y, bool value) size_t sparsemap_span(sparsemap_t *map, sparsemap_idx_t idx, size_t len, bool value) { - size_t count, nth = 0; + size_t count, nth; sm_bitvec_t vec = 0; sparsemap_idx_t offset; + nth = (idx > 0) ? sparsemap_rank(map, 0, idx - 1, value) : 0; offset = sparsemap_select(map, nth++, value); if (len == 1) { return offset; } do { - count = sparsemap_rank_vec(map, offset, offset + len, value, &vec); - if (count == len) { + count = sparsemap_rank_vec(map, offset, offset + len - 1, value, &vec); + if (count >= len) { return offset; } else { // TODO: what is nth when len > SM_BITS_PER_VECTOR? diff --git a/tests/common.c b/tests/common.c index 4450f4b..70514d9 100644 --- a/tests/common.c +++ b/tests/common.c @@ -354,9 +354,9 @@ print_bits(char *name, uint64_t value) } void -sm_bitmap_from_uint64(sparsemap_t *map, uint64_t number) +sm_bitmap_from_uint64(sparsemap_t *map, int offset, uint64_t number) { - for (int i = 0; i < 64; i++) { + for (int i = offset; i < 64; i++) { bool bit = number & ((uint64_t)1 << i); sparsemap_set(map, i, bit); } @@ -384,15 +384,15 @@ sm_add_span(sparsemap_t *map, int map_size, int span_length) } void -sm_whats_set(sparsemap_t *map, int m) +sm_whats_set(sparsemap_t *map, int off, int len) { - logf("what's set in the range [0, %d): ", m); - for (int i = 0; i < m; i++) { + printf("what's set in the range [%d, %d): ", off, off + len); + for (int i = off; i < off + len; i++) { if (sparsemap_is_set(map, i)) { - logf("%d ", i); + printf("%d ", i); } } - logf("\n"); + printf("\n"); } bool diff --git a/tests/common.h b/tests/common.h index 87b75fc..02dda1a 100644 --- a/tests/common.h +++ b/tests/common.h @@ -46,11 +46,11 @@ sparsemap_idx_t sm_add_span(sparsemap_t *map, int map_size, int span_length); void print_bits(char *name, uint64_t value); void bitmap_from_uint32(sparsemap_t *map, uint32_t number); -void sm_bitmap_from_uint64(sparsemap_t *map, uint64_t number); +void sm_bitmap_from_uint64(sparsemap_t *map, int offset, uint64_t number); uint32_t rank_uint64(uint64_t number, int n, int p); int whats_set_uint64(uint64_t number, int bitPositions[64]); -void sm_whats_set(sparsemap_t *map, int m); +void sm_whats_set(sparsemap_t *map, int off, int len); bool sm_is_span(sparsemap_t *map, sparsemap_idx_t m, int len, bool value); bool sm_occupied(sparsemap_t *map, sparsemap_idx_t m, int len, bool value); diff --git a/tests/test.c b/tests/test.c index f52d833..c3d4ef6 100644 --- a/tests/test.c +++ b/tests/test.c @@ -523,7 +523,7 @@ test_api_scan_setup(const MunitParameter params[], void *user_data) sparsemap_t *map = (sparsemap_t *)test_api_setup(params, user_data); sparsemap_init(map, buf, 1024); - sm_bitmap_from_uint64(map, ((uint64_t)0xfeedface << 32) | 0xbadc0ffee); + sm_bitmap_from_uint64(map, 0, ((uint64_t)0xfeedface << 32) | 0xbadc0ffee); return (void *)map; } @@ -610,7 +610,7 @@ test_api_select_setup(const MunitParameter params[], void *user_data) sparsemap_t *map = (sparsemap_t *)test_api_setup(params, user_data); sparsemap_init(map, buf, 1024); - sm_bitmap_from_uint64(map, ((uint64_t)0xfeedface << 32) | 0xbadc0ffee); + sm_bitmap_from_uint64(map, 0, ((uint64_t)0xfeedface << 32) | 0xbadc0ffee); return (void *)map; } @@ -648,7 +648,7 @@ test_api_select_false_setup(const MunitParameter params[], void *user_data) sparsemap_t *map = (sparsemap_t *)test_api_setup(params, user_data); sparsemap_init(map, buf, 1024); - sm_bitmap_from_uint64(map, ((uint64_t)0xfeedface << 32) | 0xbadc0ffee); + sm_bitmap_from_uint64(map, 0, ((uint64_t)0xfeedface << 32) | 0xbadc0ffee); return (void *)map; } @@ -688,7 +688,7 @@ test_api_select_neg_setup(const MunitParameter params[], void *user_data) sparsemap_t *map = (sparsemap_t *)test_api_setup(params, user_data); sparsemap_init(map, buf, 1024); - sm_bitmap_from_uint64(map, ((uint64_t)0xfeedface << 32) | 0xbadc0ffee); + sm_bitmap_from_uint64(map, 0, ((uint64_t)0xfeedface << 32) | 0xbadc0ffee); return (void *)map; } @@ -974,6 +974,7 @@ test_scale_lots_o_spans(const MunitParameter params[], void *data) return MUNIT_OK; } +#ifdef SCALE_ONDREJ static void * test_scale_ondrej_setup(const MunitParameter params[], void *user_data) { @@ -999,7 +1000,8 @@ test_scale_ondrej(const MunitParameter params[], void *data) assert_ptr_not_null(map); sparsemap_idx_t stride = 18; - sparsemap_idx_t top = 268435456; + // sparsemap_idx_t top = 268435456; + sparsemap_idx_t top = 2000; sparsemap_idx_t needle = munit_rand_int_range(1, top / stride); for (sparsemap_idx_t i = 0; i < top / stride; i += stride) { for (sparsemap_idx_t j = 0; j < stride; j++) { @@ -1010,7 +1012,7 @@ test_scale_ondrej(const MunitParameter params[], void *data) errno = 0; } } - assert_true(sm_is_span(map, i + ((i != needle) ? 10 : 9), (i != needle) ? 8 : 9, false)); + assert_true(sm_is_span(map, i + ((i != needle) ? 10 : 9), (i != needle) ? 8 : 9, true)); } sparsemap_idx_t a = sparsemap_span(map, 0, 9, false); sparsemap_idx_t l = a / stride; @@ -1018,6 +1020,31 @@ test_scale_ondrej(const MunitParameter params[], void *data) assert_true(l == needle); return MUNIT_OK; } +#endif // SCALE_ONDREJ + +static void * +test_scale_fuzz_setup(const MunitParameter params[], void *user_data) +{ + (void)params; + (void)user_data; + sparsemap_t *map = sparsemap(10 * 1024); + assert_ptr_not_null(map); + return (void *)map; +} +static void +test_scale_fuzz_tear_down(void *fixture) +{ + sparsemap_t *map = (sparsemap_t *)fixture; + assert_ptr_not_null(map); + munit_free(map); +} +static MunitResult +test_scale_fuzz(const MunitParameter params[], void *data) +{ + sparsemap_t *map = (sparsemap_t *)data; + (void)params; + return MUNIT_OK; +} static void * test_scale_spans_come_spans_go_setup(const MunitParameter params[], void *user_data) @@ -1038,7 +1065,7 @@ test_scale_spans_come_spans_go_tear_down(void *fixture) static MunitResult test_scale_spans_come_spans_go(const MunitParameter params[], void *data) { - size_t amt = 8192; // 268435456, ~5e7 interations due to 2e9 / avg(l) + size_t amt = 8192; // 268435456; // ~5e7 interations due to 2e9 / avg(l) sparsemap_t *map = (sparsemap_t *)data; (void)params; @@ -1048,7 +1075,8 @@ test_scale_spans_come_spans_go(const MunitParameter params[], void *data) int l = i % 31 + 16; sm_add_span(map, amt, l); if (errno == ENOSPC) { - map = sparsemap_set_data_size(map, sparsemap_get_capacity(map) * 2); + map = sparsemap_set_data_size(map, sparsemap_get_capacity(map) + 1024); + assert_ptr_not_null(map); errno = 0; } @@ -1275,7 +1303,10 @@ test_perf_span_tainted(const MunitParameter params[], void *data) // clang-format off static MunitTest scale_test_suite[] = { { (char *)"/lots-o-spans", test_scale_lots_o_spans, test_scale_lots_o_spans_setup, test_scale_lots_o_spans_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/ondrej", test_scale_ondrej, test_scale_ondrej_setup, test_scale_ondrej_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, +#ifdef SCALE_ONDREJ +{ (char *)"/ondrej", test_scale_ondrej, test_scale_ondrej_setup, test_scale_ondrej_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, +#endif + { (char *)"/fuzz", test_scale_fuzz, test_scale_fuzz_setup, test_scale_fuzz_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, { (char *)"/spans_come_spans_go", test_scale_spans_come_spans_go, test_scale_spans_come_spans_go_setup, test_scale_spans_come_spans_go_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, { (char *)"/best-case", test_scale_best_case, test_scale_best_case_setup, test_scale_best_case_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, { (char *)"/worst-case", test_scale_worst_case, test_scale_worst_case_setup, test_scale_worst_case_tear_down, MUNIT_TEST_OPTION_NONE, NULL },