adding soak test #5
8 changed files with 713 additions and 28 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,6 +3,7 @@
|
|||
**/*.o
|
||||
tests/test
|
||||
examples/ex_?
|
||||
examples/soak
|
||||
.cache
|
||||
hints.txt
|
||||
tmp/
|
||||
|
|
12
Makefile
12
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
|
||||
|
|
648
examples/soak.c
Normal file
648
examples/soak.c
Normal file
|
@ -0,0 +1,648 @@
|
|||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
49
tests/test.c
49
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 },
|
||||
|
|
Loading…
Reference in a new issue