diff --git a/Makefile b/Makefile index 2fe8971..fb6f455 100644 --- a/Makefile +++ b/Makefile @@ -6,10 +6,10 @@ 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 -DSPARSEMAP_ASSERT -Wall -Wextra -Wpedantic -Og -g -std=c11 -Iinclude/ -fPIC -#CFLAGS = -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=c99 -Iinclude/ -fPIC -#CFLAGS = -Wall -Wextra -Wpedantic -Og -g -fsanitize=all -fhardened -std=c99 -Iinclude/ -fPIC +#CFLAGS = -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 = -Wall -Wextra -Wpedantic -Og -g -std=c99 -Iinclude/ -Itests/ -fPIC +TEST_FLAGS = -Wall -Wextra -Wpedantic -Og -g -std=c11 -Iinclude/ -Itests/ -fPIC TESTS = tests/test TEST_OBJS = tests/test.o tests/munit.o tests/common.o @@ -49,7 +49,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 + clang-format -i src/sparsemap.c include/sparsemap.h examples/ex_*.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 @@ -76,4 +76,4 @@ 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) -# 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=c99 -Iinclude/ -fPIC +# 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 diff --git a/examples/ex_1.c b/examples/ex_1.c index 69ecc66..5fa1ae4 100644 --- a/examples/ex_1.c +++ b/examples/ex_1.c @@ -12,7 +12,6 @@ } while (0) #pragma GCC diagnostic pop - /* !!! Duplicated here for testing purposes. Keep in sync, or suffer. !!! */ struct sparsemap { uint8_t *m_data; @@ -20,7 +19,6 @@ struct sparsemap { size_t m_data_used; }; - int main() { diff --git a/include/sparsemap.h b/include/sparsemap.h index 40e9259..548aa3d 100644 --- a/include/sparsemap.h +++ b/include/sparsemap.h @@ -87,12 +87,10 @@ * Usually this is an uint64_t. */ - typedef struct sparsemap sparsemap_t; typedef uint32_t sm_idx_t; typedef uint64_t sm_bitvec_t; - /* Allocate on a sparsemap_t on the heap and initialize it. */ sparsemap_t *sparsemap(uint8_t *data, size_t size); diff --git a/src/sparsemap.c b/src/sparsemap.c index 866d8d1..eb7fd43 100644 --- a/src/sparsemap.c +++ b/src/sparsemap.c @@ -20,13 +20,12 @@ * SOFTWARE. */ +#include +#include +#include #include #include #include -#include - -#include -#include #ifdef SPARSEMAP_DIAGNOSTIC #pragma GCC diagnostic push @@ -99,18 +98,16 @@ enum __SM_CHUNK_INFO { #define SM_CHUNK_GET_FLAGS(from, at) (((from)) & ((sm_bitvec_t)SM_FLAG_MASK << ((at) * 2))) >> ((at) * 2) - typedef struct { sm_bitvec_t *m_data; } __sm_chunk_t; struct sparsemap { - uint8_t *m_data; /* The serialized bitmap data */ - size_t m_capacity; /* The total size of m_data */ - size_t m_data_used; /* The used size of m_data */ + uint8_t *m_data; /* The serialized bitmap data */ + size_t m_capacity; /* The total size of m_data */ + size_t m_data_used; /* The used size of m_data */ }; - /** * Calculates the number of sm_bitvec_ts required by a single byte with flags * (in m_data[0]). @@ -799,7 +796,8 @@ sparsemap_set_data_size(sparsemap_t *map, size_t data_size) * indicate full. */ double -sparsemap_capacity_remaining(sparsemap_t *map) { +sparsemap_capacity_remaining(sparsemap_t *map) +{ if (map->m_data_used > map->m_capacity) { return 0; } diff --git a/tests/common.c b/tests/common.c index a1834ab..6392452 100644 --- a/tests/common.c +++ b/tests/common.c @@ -2,12 +2,12 @@ #include #include +#include #include #include #include #include -#include "../include/sparsemap.h" #include "common.h" #pragma GCC diagnostic push @@ -19,6 +19,87 @@ } while (0) #pragma GCC diagnostic pop +uint64_t +tsc(void) +{ + uint32_t low, high; + __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); + return ((uint64_t)high << 32) | low; +} + +static +uint64_t get_tsc_frequency() { + uint32_t high, low; + __asm__ volatile("rdtsc" : "=a" (low), "=d" (high)); + __asm__ volatile("rdtsc"); + return ((uint64_t)high << 32) | low; +} + +double +tsc_ticks_to_ns(uint64_t tsc_ticks) { + static uint64_t tsc_freq = 0; + if (tsc_freq == 0) { + tsc_freq = get_tsc_frequency(); + } + return (double)tsc_ticks / (double)tsc_freq * 1e9; +} + +void +est_sift_up(uint64_t *heap, int child_index) +{ + while (child_index > 0) { + int parent_index = (child_index - 1) / 2; + if (heap[parent_index] > heap[child_index]) { + // Swap parent and child + uint64_t temp = heap[parent_index]; + heap[parent_index] = heap[child_index]; + heap[child_index] = temp; + child_index = parent_index; + } else { + break; // Heap property satisfied + } + } +} + +void +est_sift_down(uint64_t *heap, int heap_size, int parent_index) +{ + int child_index = 2 * parent_index + 1; // Left child + while (child_index < heap_size) { + // Right child exists and is smaller than left child + if (child_index + 1 < heap_size && heap[child_index + 1] < heap[child_index]) { + child_index++; + } + // If the smallest child is smaller than the parent, swap them + if (heap[child_index] < heap[parent_index]) { + uint64_t temp = heap[child_index]; + heap[child_index] = heap[parent_index]; + heap[parent_index] = temp; + parent_index = child_index; + child_index = 2 * parent_index + 1; + } else { + break; // Heap property satisfied + } + } +} + +void +est_insert_value(uint64_t *heap, int heap_max_size, int *heap_size, uint64_t value) +{ + if (*heap_size < heap_max_size) { // Heap not full, insert value + heap[*heap_size] = value; + est_sift_up(heap, *heap_size); + (*heap_size)++; + } else { + // Heap is full, replace root with new value with a certain probability + // This is a very naive approach to maintain a sample of the input + if (rand() % 2) { + heap[0] = value; + est_sift_down(heap, heap_max_size, 0); + } + } +} + int __xorshift32_state = 0; // Xorshift algorithm for PRNG @@ -120,7 +201,11 @@ int create_sequential_set_in_empty_map(sparsemap_t *map, int s, int r) { int placed_at; - placed_at = random_uint32() % (s - r - 1); + if (s >= r + 1) { + placed_at = 0; + } else { + placed_at = random_uint32() % (s - r - 1); + } for (int i = placed_at; i < placed_at + r; i++) { sparsemap_set(map, i, true); } @@ -269,60 +354,62 @@ setup_test_array(int a[], int l, int max_value) } void -bitmap_from_uint32(sparsemap_t *map, uint32_t number) { - for (int i = 0; i < 32; i++) { - bool bit = number & (1 << i); - sparsemap_set(map, i, bit); - } +bitmap_from_uint32(sparsemap_t *map, uint32_t number) +{ + for (int i = 0; i < 32; i++) { + bool bit = number & (1 << i); + sparsemap_set(map, i, bit); + } } void -bitmap_from_uint64(sparsemap_t *map, uint64_t number) { - for (int i = 0; i < 64; i++) { - bool bit = number & (1 << i); - sparsemap_set(map, i, bit); - } +bitmap_from_uint64(sparsemap_t *map, uint64_t number) +{ + for (int i = 0; i < 64; i++) { + bool bit = number & (1 << i); + sparsemap_set(map, i, bit); + } } uint32_t rank_uint64(uint64_t number, int n, int p) { - if (p < n || p > 63) { - return 0; - } + if (p < n || p > 63) { + return 0; + } - /* Create a mask for the range between n and p. - This works by shifting 1 to the left (p+1) times, subtracting 1 to have - a sequence of p 1's, then shifting n times to the left to position it - starting at n. Finally, subtracting (1 << n) - 1 removes the bits below - n from the mask. */ - uint64_t mask = ((uint64_t)1 << (p + 1)) - 1 - (((uint64_t)1 << n) - 1); + /* Create a mask for the range between n and p. + This works by shifting 1 to the left (p+1) times, subtracting 1 to have + a sequence of p 1's, then shifting n times to the left to position it + starting at n. Finally, subtracting (1 << n) - 1 removes the bits below + n from the mask. */ + uint64_t mask = ((uint64_t)1 << (p + 1)) - 1 - (((uint64_t)1 << n) - 1); - /* Apply the mask and count the set bits in the result. */ - uint64_t maskedNumber = number & mask; + /* Apply the mask and count the set bits in the result. */ + uint64_t maskedNumber = number & mask; - /* Count the bits set in maskedNumber. */ - uint32_t count = 0; - while (maskedNumber) { - count += maskedNumber & 1; - maskedNumber >>= 1; - } + /* Count the bits set in maskedNumber. */ + uint32_t count = 0; + while (maskedNumber) { + count += maskedNumber & 1; + maskedNumber >>= 1; + } - return count; + return count; } int whats_set_uint64(uint64_t number, int pos[64]) { - int length = 0; + int length = 0; - for (int i = 0; i < 64; i++) { - if (number & ((uint64_t)1 << i)) { - pos[length++] = i; - } + for (int i = 0; i < 64; i++) { + if (number & ((uint64_t)1 << i)) { + pos[length++] = i; } + } - return length; + return length; } void diff --git a/tests/common.h b/tests/common.h index bbeac10..17b27b1 100644 --- a/tests/common.h +++ b/tests/common.h @@ -23,6 +23,21 @@ #define XORSHIFT_SEED_VALUE ((unsigned int)time(NULL) ^ getpid()) #endif +#define EST_MEDIAN_DECL(decl, size) \ + uint64_t heap_##decl[size] = { 0 }; \ + int heap_##decl##_max_size = size; \ + int heap_##decl##_size = 0; + +#define EST_MEDIAN_ADD(decl, value) est_insert_value(heap_##decl, heap_##decl##_max_size, &heap_##decl##_size, (value)); + +#define EST_MEDIAN_GET(decl) heap_##decl[0] + +uint64_t tsc(void); +double tsc_ticks_to_ns(uint64_t tsc_ticks); +void est_sift_up(uint64_t *heap, int child_index); +void est_sift_down(uint64_t *heap, int heap_size, int parent_index); +void est_insert_value(uint64_t *heap, int heap_max_size, int *heap_size, uint64_t value); + void xorshift32_seed(); uint32_t xorshift32(); diff --git a/tests/test.c b/tests/test.c index 467f5e4..f84fb23 100644 --- a/tests/test.c +++ b/tests/test.c @@ -21,7 +21,6 @@ #pragma warning(disable : 4127) #endif - /* !!! Duplicated here for testing purposes. Keep in sync, or suffer. !!! */ struct sparsemap { uint8_t *m_data; @@ -39,7 +38,7 @@ populate_map(sparsemap_t *map, int size, int max_value) int array[size]; setup_test_array(array, size, max_value); - //TODO ensure_sequential_set(array, size, 10); + // TODO ensure_sequential_set(array, size, 10); shuffle(array, size); for (int i = 0; i < size; i++) { sparsemap_set(map, array[i], true); @@ -70,7 +69,7 @@ static MunitResult test_api_static_init(const MunitParameter params[], void *data) { sparsemap_t a_map, *map = &a_map; - uint8_t buf[1024] = {0}; + uint8_t buf[1024] = { 0 }; (void)params; (void)data; @@ -215,7 +214,7 @@ test_api_remaining_capacity(const MunitParameter params[], void *data) sparsemap_set(map, i++, true); cap = sparsemap_capacity_remaining(map); } while (cap > 1.0); - //assert_true(i == 169985); when seed is 8675309 + // assert_true(i == 169985); when seed is 8675309 assert_true(cap <= 1.0); sparsemap_clear(map); @@ -226,7 +225,7 @@ test_api_remaining_capacity(const MunitParameter params[], void *data) i++; cap = sparsemap_capacity_remaining(map); } while (cap > 2.0); - //assert_true(i == 64); when seed is 8675309 + // assert_true(i == 64); when seed is 8675309 assert_true(cap <= 2.0); return MUNIT_OK; @@ -422,7 +421,8 @@ test_api_scan_tear_down(void *fixture) test_api_tear_down(fixture); } void -scan_for_0xfeedfacebadcoffee(sm_idx_t v[], size_t n) { +scan_for_0xfeedfacebadcoffee(sm_idx_t v[], size_t n) +{ /* Called multiple times */ ((void)v); ((void)n); @@ -449,7 +449,7 @@ test_api_split_setup(const MunitParameter params[], void *user_data) sparsemap_t *map = (sparsemap_t *)test_api_setup(params, user_data); sparsemap_init(map, buf, 1024); - for(int i = 0; i < 1024; i ++) { + for (int i = 0; i < 1024; i++) { sparsemap_set(map, i, true); } return (void *)map; @@ -465,7 +465,7 @@ static MunitResult test_api_split(const MunitParameter params[], void *data) { sparsemap_t *map = (sparsemap_t *)data; - uint8_t buf[1024] = {0}; + uint8_t buf[1024] = { 0 }; sparsemap_t portion; (void)params; @@ -600,58 +600,174 @@ test_api_span(const MunitParameter params[], void *data) assert_ptr_not_null(map); - int located_at, placed_at, amt = 5000; - for (int i = 1; i < amt; i++) { - for (int j = 1; j < amt / 10; j++) { - sparsemap_clear(map); - placed_at = create_sequential_set_in_empty_map(map, amt, j); - //logf("i = %d, j = %d\tplaced_at %d\n", i, j, placed_at); - //whats_set(map, 5000); - located_at = sparsemap_span(map, 0, j); - if (placed_at != located_at) - logf("a: i = %d, j = %d\tplaced_at %d located_at %d\n", i, j, placed_at, located_at); - assert_true(located_at == placed_at); - } - } -/* - for (int i = 1; i < amt; i++) { - for (int j = 1; j < amt / 10; j++) { - sparsemap_clear(map); - populate_map(map, 1024, 3 * 1024); - placed_at = create_sequential_set_in_empty_map(map, amt, j); - located_at = sparsemap_span(map, 0, j); - if (located_at >= placed_at) - logf("b: i = %d, j = %d\tplaced_at %d located_at %d\n", i, j, placed_at, located_at); - //assert_true(located_at >= placed_at); - located_at = sparsemap_span(map, (placed_at < j ? 0 : placed_at / 2), i); - assert_true(placed_at == located_at); - } - } - */ + int located_at, placed_at, amt = 10000; - return MUNIT_OK; + placed_at = create_sequential_set_in_empty_map(map, amt, 1); + located_at = sparsemap_span(map, 0, 1); + assert_true(located_at == placed_at); + + sparsemap_clear(map); + + placed_at = create_sequential_set_in_empty_map(map, amt, 50); + located_at = sparsemap_span(map, 0, 50); + assert_true(located_at == placed_at); + + sparsemap_clear(map); + + placed_at = create_sequential_set_in_empty_map(map, amt, 50); + located_at = sparsemap_span(map, placed_at / 2, 50); + assert_true(located_at == placed_at); + + /* TODO + sparsemap_clear(map); + + placed_at = create_sequential_set_in_empty_map(map, amt, amt - 1); + located_at = sparsemap_span(map, 0, amt - 1); + assert_true(located_at == placed_at); + */ + + return MUNIT_OK; } -static MunitTest api_test_suite[] = { { (char *)"/api/static_init", test_api_static_init, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/clear", test_api_clear, test_api_clear_setup, test_api_clear_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/open", test_api_open, test_api_open_setup, test_api_open_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/set_data_size", test_api_set_data_size, test_api_set_data_size_setup, test_api_set_data_size_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/remaining_capacity", test_api_remaining_capacity, test_api_remaining_capacity_setup, test_api_remaining_capacity_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/get_range_size", test_api_get_range_size, test_api_get_range_size_setup, test_api_get_range_size_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/is_set", test_api_is_set, test_api_is_set_setup, test_api_is_set_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/set", test_api_set, test_api_set_setup, test_api_set_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/get_start_offset", test_api_get_start_offset, test_api_get_start_offset_setup, test_api_get_start_offset_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/get_size", test_api_get_size, test_api_get_size_setup, test_api_get_size_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/scan", test_api_scan, test_api_scan_setup, test_api_scan_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/split", test_api_split, test_api_split_setup, test_api_split_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/select", test_api_select, test_api_select_setup, test_api_select_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/rank", test_api_rank, test_api_rank_setup, test_api_rank_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/span", test_api_span, test_api_span_setup, test_api_span_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, +// clang-format off +static MunitTest api_test_suite[] = { + { (char *)"/static_init", test_api_static_init, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/clear", test_api_clear, test_api_clear_setup, test_api_clear_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/open", test_api_open, test_api_open_setup, test_api_open_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/set_data_size", test_api_set_data_size, test_api_set_data_size_setup, test_api_set_data_size_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/remaining_capacity", test_api_remaining_capacity, test_api_remaining_capacity_setup, test_api_remaining_capacity_tear_down, + MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/get_range_size", test_api_get_range_size, test_api_get_range_size_setup, test_api_get_range_size_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/is_set", test_api_is_set, test_api_is_set_setup, test_api_is_set_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/set", test_api_set, test_api_set_setup, test_api_set_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/get_start_offset", test_api_get_start_offset, test_api_get_start_offset_setup, test_api_get_start_offset_tear_down, MUNIT_TEST_OPTION_NONE, + NULL }, + { (char *)"/get_size", test_api_get_size, test_api_get_size_setup, test_api_get_size_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/scan", test_api_scan, test_api_scan_setup, test_api_scan_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/split", test_api_split, test_api_split_setup, test_api_split_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/select", test_api_select, test_api_select_setup, test_api_select_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/rank", test_api_rank, test_api_rank_setup, test_api_rank_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/span", test_api_span, test_api_span_setup, test_api_span_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } +}; +// clang-format on + +static void * +test_perf_span_solo_setup(const MunitParameter params[], void *user_data) +{ + uint8_t *buf = munit_calloc(1024 * 3, sizeof(uint8_t)); + sparsemap_t *map = (sparsemap_t *)test_api_setup(params, user_data); + + sparsemap_init(map, buf, 3 * 1024); + + return (void *)map; +} +static void +test_perf_span_solo_tear_down(void *fixture) +{ + sparsemap_t *map = (sparsemap_t *)fixture; + free(map->m_data); + test_api_tear_down(fixture); +} +EST_MEDIAN_DECL(solo, 10000) +static MunitResult +test_perf_span_solo(const MunitParameter params[], void *data) +{ + sparsemap_t *map = (sparsemap_t *)data; + uint64_t stop, start; + (void)params; + int located_at, placed_at, amt = 1000; + + assert_ptr_not_null(map); + + for (int i = 1; i < amt; i++) { + for (int j = 1; j < amt / 10; j++) { + sparsemap_clear(map); + placed_at = create_sequential_set_in_empty_map(map, amt, j); + // logf("i = %d, j = %d\tplaced_at %d\n", i, j, placed_at); + // whats_set(map, 5000); + start = tsc(); + located_at = sparsemap_span(map, 0, j); + stop = tsc(); + // fprintf(stdout, "%ll - %ll = %ll\n", stop, start, stop - start); + EST_MEDIAN_ADD(solo, stop - start); + if (placed_at != located_at) + logf("a: i = %d, j = %d\tplaced_at %d located_at %d\n", i, j, placed_at, located_at); + } + } + uint64_t est = EST_MEDIAN_GET(solo); + fprintf(stdout, "median time %zu or %f ns\n", est, tsc_ticks_to_ns(est)); // measured 228 + assert_true(EST_MEDIAN_GET(solo) < 500); + fflush(stdout); + + return MUNIT_OK; +} + +static void * +test_perf_span_tainted_setup(const MunitParameter params[], void *user_data) +{ + uint8_t *buf = munit_calloc(1024 * 3, sizeof(uint8_t)); + sparsemap_t *map = (sparsemap_t *)test_api_setup(params, user_data); + + sparsemap_init(map, buf, 3 * 1024); + + return (void *)map; +} +static void +test_perf_span_tainted_tear_down(void *fixture) +{ + sparsemap_t *map = (sparsemap_t *)fixture; + free(map->m_data); + test_api_tear_down(fixture); +} +EST_MEDIAN_DECL(tainted, 10000) +static MunitResult +test_perf_span_tainted(const MunitParameter params[], void *data) +{ + sparsemap_t *map = (sparsemap_t *)data; + uint64_t stop, start; + (void)params; + + assert_ptr_not_null(map); + + int located_at, placed_at, amt = 1000; + for (int i = 1; i < amt; i++) { + for (int j = 1; j < amt / 10; j++) { + sparsemap_clear(map); + populate_map(map, 1024, 1 * 1024); + placed_at = create_sequential_set_in_empty_map(map, amt, j); + start = tsc(); + located_at = sparsemap_span(map, 0, j); + stop = tsc(); + EST_MEDIAN_ADD(tainted, stop - start); + if (located_at >= placed_at) + logf("b: i = %d, j = %d\tplaced_at %d located_at %d\n", i, j, placed_at, located_at); + // assert_true(located_at >= placed_at); + //start = tsc(); + //located_at = sparsemap_span(map, (placed_at < j ? 0 : placed_at / 2), i); + //stop = tsc(); + //EST_MEDIAN_ADD(solo, stop - start); + // assert_true(placed_at == located_at); + } + } + uint64_t est = EST_MEDIAN_GET(tainted); + fprintf(stdout, "median time %zu or %f ns\n", est, tsc_ticks_to_ns(est)); // measured 228 + + return MUNIT_OK; +} + +// clang-format off +static MunitTest performance_test_suite[] = { + { (char *)"/span/solo", test_perf_span_solo, test_perf_span_solo_setup, test_perf_span_solo_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/span/tainted", test_perf_span_tainted, test_perf_span_tainted_setup, test_perf_span_tainted_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } }; +// clang-format on -static MunitTest scale_tests[] = { { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } }; - -static MunitSuite other_test_suite[] = { { "/scale", scale_tests, NULL, 1, MUNIT_SUITE_OPTION_NONE }, { NULL, NULL, NULL, 0, MUNIT_SUITE_OPTION_NONE } }; +// clang-format off +static MunitSuite other_test_suite[] = { + { "/performance", performance_test_suite, NULL, 1, MUNIT_SUITE_OPTION_NONE }, + { NULL, NULL, NULL, 0, MUNIT_SUITE_OPTION_NONE } }; +// clang-format on static const MunitSuite main_test_suite = { (char *)"/api", api_test_suite, other_test_suite, 1, MUNIT_SUITE_OPTION_NONE };