Skip to content

Instantly share code, notes, and snippets.

@michael-grunder
Created July 21, 2026 19:46
Show Gist options
  • Select an option

  • Save michael-grunder/e3819c48617f130be8e2c75beee75ed8 to your computer and use it in GitHub Desktop.

Select an option

Save michael-grunder/e3819c48617f130be8e2c75beee75ed8 to your computer and use it in GitHub Desktop.
Granular ck_rhs_t benchmark script
/*
* Copyright 2026 Michael Grunder.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* compile with:
* cc -o ck_rhs_mixed_benchmark -O2 -g3 ck_rhs_mixed_benchmark.c src/ck_rhs.c
*/
#include <ck_malloc.h>
#include <ck_rhs.h>
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEFAULT_OPERATIONS UINT64_C(1000000)
#define INITIAL_CAPACITY 1024
#define BATCH_SIZE 1024
enum api_operation {
API_INIT,
API_DESTROY,
API_PUT,
API_SET,
API_FAS,
API_GET,
API_REMOVE,
API_COUNT,
API_ITERATOR_INIT,
API_NEXT,
API_OPERATIONS
};
enum random_operation {
RANDOM_PUT,
RANDOM_SET,
RANDOM_FAS,
RANDOM_GET,
RANDOM_REMOVE,
RANDOM_OPERATIONS
};
enum meta_operation {
META_ITERATION,
META_BATCH_DELETE,
META_FULL_VALIDATION,
META_OPERATIONS
};
struct measurement {
uint64_t calls;
uint64_t cycles;
};
struct allocation_metrics {
uint64_t requests;
uint64_t requested_bytes;
uint64_t frees;
uint64_t freed_bytes;
uint64_t live_bytes;
uint64_t peak_live_bytes;
};
struct key {
uint64_t id;
unsigned long hash;
};
struct key_slot {
struct key key[2];
size_t index;
unsigned int version;
bool active;
uint64_t seen_epoch;
};
struct key_pool {
struct key_slot *slots;
struct key_slot **active;
struct key_slot **inactive;
size_t capacity;
size_t active_count;
size_t inactive_count;
};
static const char *api_names[API_OPERATIONS] = {
"ck_rhs_init",
"ck_rhs_destroy",
"ck_rhs_put",
"ck_rhs_set",
"ck_rhs_fas",
"ck_rhs_get",
"ck_rhs_remove",
"ck_rhs_count",
"ck_rhs_iterator_init",
"ck_rhs_next"
};
static const char *random_names[RANDOM_OPERATIONS] = {
"put", "set", "fas", "get", "remove"
};
static const char *meta_names[META_OPERATIONS] = {
"iteration", "batch delete", "full validation"
};
static struct measurement api_measurements[API_OPERATIONS];
static struct measurement random_measurements[RANDOM_OPERATIONS];
static struct measurement meta_measurements[META_OPERATIONS];
static struct allocation_metrics allocation_metrics;
static uint64_t rhs_seed;
static uint64_t seen_epoch;
static bool allocation_tracking_enabled;
static bool measurement_enabled;
static uint64_t
random_next(uint64_t *state)
{
uint64_t z;
z = (*state += UINT64_C(0x9e3779b97f4a7c15));
z = (z ^ (z >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
z = (z ^ (z >> 27)) * UINT64_C(0x94d049bb133111eb);
return z ^ (z >> 31);
}
static uint64_t
mix64(uint64_t value)
{
value = (value ^ (value >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
value = (value ^ (value >> 27)) * UINT64_C(0x94d049bb133111eb);
return value ^ (value >> 31);
}
static uint64_t
benchmark_timestamp(void)
{
#if defined(__x86_64__)
uint32_t low, high;
/* Avoid the heavyweight cpuid pair in common.h for per-call samples. */
__asm__ __volatile__("lfence; rdtsc"
: "=a" (low), "=d" (high)
:
: "memory");
return ((uint64_t)high << 32) | low;
#else
return rdtsc();
#endif
}
static void
benchmark_abort(const char *message)
{
fprintf(stderr, "ERROR: %s\n", message);
exit(EXIT_FAILURE);
}
static void *
benchmark_malloc(size_t size)
{
void *pointer;
if (allocation_tracking_enabled) {
allocation_metrics.requests++;
allocation_metrics.requested_bytes += size;
}
pointer = malloc(size);
if (pointer == NULL)
return NULL;
if (allocation_tracking_enabled) {
allocation_metrics.live_bytes += size;
if (allocation_metrics.live_bytes >
allocation_metrics.peak_live_bytes) {
allocation_metrics.peak_live_bytes =
allocation_metrics.live_bytes;
}
}
return pointer;
}
static void
benchmark_free(void *pointer, size_t size, bool defer)
{
(void)defer;
if (allocation_tracking_enabled) {
allocation_metrics.frees++;
allocation_metrics.freed_bytes += size;
if (size > allocation_metrics.live_bytes)
benchmark_abort("allocator byte accounting underflow");
allocation_metrics.live_bytes -= size;
}
free(pointer);
}
static struct ck_malloc allocator = {
.malloc = benchmark_malloc,
.free = benchmark_free
};
static unsigned long
key_hash(const void *object, unsigned long seed)
{
const struct key *key = object;
(void)seed;
return key->hash;
}
static bool
key_compare(const void *left, const void *right)
{
const struct key *a = left;
const struct key *b = right;
return a->id == b->id;
}
static unsigned long
hash_for_id(uint64_t id)
{
/* Exercise clustered probes without constraining the total hash space. */
return (unsigned long)mix64(id ^ rhs_seed) & ~0x7UL;
}
static struct key *
slot_key(struct key_slot *slot)
{
return &slot->key[slot->version];
}
static struct key *
slot_alternate_key(struct key_slot *slot)
{
return &slot->key[slot->version ^ 1U];
}
static void
pool_init(struct key_pool *pool, size_t capacity)
{
struct key_slot *slot;
size_t i;
memset(pool, 0, sizeof(*pool));
pool->slots = calloc(capacity, sizeof(*pool->slots));
pool->active = malloc(capacity * sizeof(*pool->active));
pool->inactive = malloc(capacity * sizeof(*pool->inactive));
if (pool->slots == NULL || pool->active == NULL || pool->inactive == NULL)
benchmark_abort("failed to allocate key pool");
pool->capacity = capacity;
pool->inactive_count = capacity;
for (i = 0; i < capacity; i++) {
slot = &pool->slots[i];
slot->key[0].id = slot->key[1].id = (uint64_t)i + 1;
slot->key[0].hash = slot->key[1].hash =
hash_for_id((uint64_t)i + 1);
slot->index = i;
pool->inactive[i] = slot;
}
}
static void
pool_destroy(struct key_pool *pool)
{
free(pool->inactive);
free(pool->active);
free(pool->slots);
}
static struct key_slot *
pool_select(struct key_pool *pool, uint64_t *random, bool *exists)
{
*exists = pool->active_count != 0 &&
(pool->inactive_count == 0 || (random_next(random) & 1) != 0);
if (*exists) {
return pool->active[random_next(random) % pool->active_count];
}
return pool->inactive[random_next(random) % pool->inactive_count];
}
static void
pool_activate(struct key_pool *pool, struct key_slot *slot,
unsigned int version)
{
struct key_slot *moved;
if (slot->active)
benchmark_abort("activated an active key slot");
pool->inactive_count--;
moved = pool->inactive[pool->inactive_count];
pool->inactive[slot->index] = moved;
moved->index = slot->index;
slot->active = true;
slot->version = version;
slot->index = pool->active_count;
pool->active[pool->active_count++] = slot;
}
static void
pool_deactivate(struct key_pool *pool, struct key_slot *slot)
{
struct key_slot *moved;
if (slot->active == false)
benchmark_abort("deactivated an inactive key slot");
pool->active_count--;
moved = pool->active[pool->active_count];
pool->active[slot->index] = moved;
moved->index = slot->index;
slot->active = false;
slot->index = pool->inactive_count;
pool->inactive[pool->inactive_count++] = slot;
}
static struct key_slot *
pool_slot_for_key(struct key_pool *pool, const struct key *key)
{
struct key_slot *slot;
if (key->id == 0 || key->id > pool->capacity)
benchmark_abort("ck_rhs returned a key outside the key pool");
slot = &pool->slots[key->id - 1];
if (slot->active == false || slot_key(slot) != key)
benchmark_abort("ck_rhs returned a stale key pointer");
return slot;
}
static uint64_t
measurement_start(struct measurement *measurement)
{
if (measurement_enabled == false)
return 0;
measurement->calls++;
return benchmark_timestamp();
}
static void
measurement_stop(struct measurement *measurement, uint64_t start)
{
if (measurement_enabled)
measurement->cycles += benchmark_timestamp() - start;
}
static bool
timed_rhs_init(ck_rhs_t *rhs, unsigned long capacity)
{
uint64_t start;
bool result;
start = measurement_start(&api_measurements[API_INIT]);
result = ck_rhs_init(rhs, CK_RHS_MODE_SPMC | CK_RHS_MODE_OBJECT,
key_hash, key_compare, &allocator, capacity, (unsigned long)rhs_seed);
measurement_stop(&api_measurements[API_INIT], start);
return result;
}
static void
timed_rhs_destroy(ck_rhs_t *rhs)
{
uint64_t start;
start = measurement_start(&api_measurements[API_DESTROY]);
ck_rhs_destroy(rhs);
measurement_stop(&api_measurements[API_DESTROY], start);
}
static bool
timed_rhs_put(ck_rhs_t *rhs, unsigned long hash, const void *key)
{
uint64_t start;
bool result;
start = measurement_start(&api_measurements[API_PUT]);
result = ck_rhs_put(rhs, hash, key);
measurement_stop(&api_measurements[API_PUT], start);
return result;
}
static bool
timed_rhs_set(ck_rhs_t *rhs, unsigned long hash, const void *key,
void **previous)
{
uint64_t start;
bool result;
start = measurement_start(&api_measurements[API_SET]);
result = ck_rhs_set(rhs, hash, key, previous);
measurement_stop(&api_measurements[API_SET], start);
return result;
}
static bool
timed_rhs_fas(ck_rhs_t *rhs, unsigned long hash, const void *key,
void **previous)
{
uint64_t start;
bool result;
start = measurement_start(&api_measurements[API_FAS]);
result = ck_rhs_fas(rhs, hash, key, previous);
measurement_stop(&api_measurements[API_FAS], start);
return result;
}
static void *
timed_rhs_get(ck_rhs_t *rhs, unsigned long hash, const void *key)
{
uint64_t start;
void *result;
start = measurement_start(&api_measurements[API_GET]);
result = ck_rhs_get(rhs, hash, key);
measurement_stop(&api_measurements[API_GET], start);
return result;
}
static void *
timed_rhs_remove(ck_rhs_t *rhs, unsigned long hash, const void *key)
{
uint64_t start;
void *result;
start = measurement_start(&api_measurements[API_REMOVE]);
result = ck_rhs_remove(rhs, hash, key);
measurement_stop(&api_measurements[API_REMOVE], start);
return result;
}
static unsigned long
timed_rhs_count(ck_rhs_t *rhs)
{
uint64_t start;
unsigned long result;
start = measurement_start(&api_measurements[API_COUNT]);
result = ck_rhs_count(rhs);
measurement_stop(&api_measurements[API_COUNT], start);
return result;
}
static void
timed_rhs_iterator_init(ck_rhs_iterator_t *iterator)
{
uint64_t start;
start = measurement_start(&api_measurements[API_ITERATOR_INIT]);
ck_rhs_iterator_init(iterator);
measurement_stop(&api_measurements[API_ITERATOR_INIT], start);
}
static bool
timed_rhs_next(ck_rhs_t *rhs, ck_rhs_iterator_t *iterator, void **key)
{
uint64_t start;
bool result;
start = measurement_start(&api_measurements[API_NEXT]);
result = ck_rhs_next(rhs, iterator, key);
measurement_stop(&api_measurements[API_NEXT], start);
return result;
}
static void
validate_count(ck_rhs_t *rhs, const struct key_pool *pool)
{
if (timed_rhs_count(rhs) != pool->active_count)
benchmark_abort("ck_rhs_count did not match the key pool");
}
static void
validate_all(ck_rhs_t *rhs, struct key_pool *pool)
{
ck_rhs_iterator_t iterator;
struct key_slot *slot;
struct key *key;
uint64_t start;
size_t iterated, i;
start = measurement_start(&meta_measurements[META_FULL_VALIDATION]);
validate_count(rhs, pool);
seen_epoch++;
if (seen_epoch == 0)
benchmark_abort("iteration epoch overflow");
iterated = 0;
timed_rhs_iterator_init(&iterator);
while (timed_rhs_next(rhs, &iterator, (void **)&key)) {
slot = pool_slot_for_key(pool, key);
if (slot->seen_epoch == seen_epoch)
benchmark_abort("iterator returned a key more than once");
slot->seen_epoch = seen_epoch;
iterated++;
}
if (iterated != pool->active_count)
benchmark_abort("iterator did not return every active key");
for (i = 0; i < pool->active_count; i++) {
slot = pool->active[i];
if (slot->seen_epoch != seen_epoch)
benchmark_abort("iterator missed an active key");
key = slot_key(slot);
if (timed_rhs_get(rhs, key->hash, key) != key)
benchmark_abort("ck_rhs_get missed an active key");
}
measurement_stop(&meta_measurements[META_FULL_VALIDATION], start);
}
static void
operation_put(ck_rhs_t *rhs, struct key_pool *pool, uint64_t *random)
{
struct key_slot *slot;
struct key *candidate, *expected;
uint64_t start;
unsigned int version;
bool exists, result;
start = measurement_start(&random_measurements[RANDOM_PUT]);
slot = pool_select(pool, random, &exists);
expected = exists ? slot_key(slot) : NULL;
version = slot->version ^ 1U;
candidate = &slot->key[version];
result = timed_rhs_put(rhs, candidate->hash, candidate);
if (result == exists)
benchmark_abort("ck_rhs_put returned an unexpected result");
if (result)
pool_activate(pool, slot, version);
if (timed_rhs_get(rhs, candidate->hash, candidate) !=
(result ? candidate : expected)) {
benchmark_abort("ck_rhs_put left an unexpected value");
}
measurement_stop(&random_measurements[RANDOM_PUT], start);
}
static void
operation_set(ck_rhs_t *rhs, struct key_pool *pool, uint64_t *random)
{
struct key_slot *slot;
struct key *candidate, *expected;
void *previous;
uint64_t start;
unsigned int version;
bool exists;
start = measurement_start(&random_measurements[RANDOM_SET]);
slot = pool_select(pool, random, &exists);
expected = exists ? slot_key(slot) : NULL;
version = slot->version ^ 1U;
candidate = &slot->key[version];
previous = (void *)(uintptr_t)1;
if (timed_rhs_set(rhs, candidate->hash, candidate, &previous) == false)
benchmark_abort("ck_rhs_set failed");
if (previous != expected)
benchmark_abort("ck_rhs_set returned an unexpected previous value");
if (exists)
slot->version = version;
else
pool_activate(pool, slot, version);
if (timed_rhs_get(rhs, candidate->hash, candidate) != candidate)
benchmark_abort("ck_rhs_set left an unexpected value");
measurement_stop(&random_measurements[RANDOM_SET], start);
}
static void
operation_fas(ck_rhs_t *rhs, struct key_pool *pool, uint64_t *random)
{
struct key_slot *slot;
struct key *candidate, *expected;
void *previous;
uint64_t start;
unsigned int version;
bool exists, result;
start = measurement_start(&random_measurements[RANDOM_FAS]);
slot = pool_select(pool, random, &exists);
expected = exists ? slot_key(slot) : NULL;
version = slot->version ^ 1U;
candidate = &slot->key[version];
previous = (void *)(uintptr_t)1;
result = timed_rhs_fas(rhs, candidate->hash, candidate, &previous);
if (result != exists)
benchmark_abort("ck_rhs_fas returned an unexpected result");
if (result) {
if (previous != expected)
benchmark_abort("ck_rhs_fas returned the wrong previous value");
slot->version = version;
} else if (previous != NULL) {
benchmark_abort("failed ck_rhs_fas returned a previous value");
}
if (timed_rhs_get(rhs, candidate->hash, candidate) !=
(result ? candidate : expected)) {
benchmark_abort("ck_rhs_fas left an unexpected value");
}
measurement_stop(&random_measurements[RANDOM_FAS], start);
}
static void
operation_get(ck_rhs_t *rhs, struct key_pool *pool, uint64_t *random)
{
struct key_slot *slot;
struct key *probe, *expected;
uint64_t start;
bool exists;
start = measurement_start(&random_measurements[RANDOM_GET]);
slot = pool_select(pool, random, &exists);
probe = slot_alternate_key(slot);
expected = exists ? slot_key(slot) : NULL;
if (timed_rhs_get(rhs, probe->hash, probe) != expected)
benchmark_abort("ck_rhs_get returned an unexpected value");
measurement_stop(&random_measurements[RANDOM_GET], start);
}
static void
operation_remove(ck_rhs_t *rhs, struct key_pool *pool, uint64_t *random)
{
struct key_slot *slot;
struct key *probe, *expected;
void *removed;
uint64_t start;
bool exists;
start = measurement_start(&random_measurements[RANDOM_REMOVE]);
slot = pool_select(pool, random, &exists);
probe = slot_alternate_key(slot);
expected = exists ? slot_key(slot) : NULL;
removed = timed_rhs_remove(rhs, probe->hash, probe);
if (removed != expected)
benchmark_abort("ck_rhs_remove returned an unexpected value");
if (removed != NULL) {
pool_deactivate(pool, slot);
if (timed_rhs_get(rhs, probe->hash, probe) != NULL)
benchmark_abort("ck_rhs_remove left a reachable value");
}
measurement_stop(&random_measurements[RANDOM_REMOVE], start);
}
static void
operation_iteration(ck_rhs_t *rhs, struct key_pool *pool)
{
uint64_t start;
start = measurement_start(&meta_measurements[META_ITERATION]);
validate_all(rhs, pool);
measurement_stop(&meta_measurements[META_ITERATION], start);
}
static void
operation_batch_delete(ck_rhs_t *rhs, struct key_pool *pool)
{
struct key *keys[BATCH_SIZE], *key;
struct key_slot *slot;
ck_rhs_iterator_t iterator;
uint64_t start;
unsigned long before;
size_t position, i;
start = measurement_start(&meta_measurements[META_BATCH_DELETE]);
while (timed_rhs_count(rhs) > 0) {
validate_count(rhs, pool);
before = timed_rhs_count(rhs);
timed_rhs_iterator_init(&iterator);
position = 0;
while (position < BATCH_SIZE &&
timed_rhs_next(rhs, &iterator, (void **)&keys[position])) {
pool_slot_for_key(pool, keys[position]);
position++;
}
if (position == 0)
benchmark_abort("batch deletion made no progress");
for (i = 0; i < position; i++) {
key = keys[i];
slot = pool_slot_for_key(pool, key);
if (timed_rhs_remove(rhs, key->hash, key) != key)
benchmark_abort("batch deletion missed a key");
pool_deactivate(pool, slot);
}
if (timed_rhs_count(rhs) != before - position)
benchmark_abort("batch deletion count mismatch");
}
if (pool->active_count != 0)
benchmark_abort("batch deletion left active keys");
measurement_stop(&meta_measurements[META_BATCH_DELETE], start);
}
static uint64_t
parse_u64(const char *option, const char *value)
{
char *end;
uintmax_t parsed;
errno = 0;
end = NULL;
parsed = strtoumax(value, &end, 0);
if (errno != 0 || value[0] == '-' || end == value || *end != '\0' ||
parsed > UINT64_MAX) {
fprintf(stderr, "Invalid value for %s: %s\n", option, value);
exit(EXIT_FAILURE);
}
return (uint64_t)parsed;
}
static void
usage(FILE *stream, const char *program_name)
{
fprintf(stream,
"Usage: %s [--ops NUMBER] [--seed NUMBER]\n"
"\n"
" --ops NUMBER Randomized operations (default: %" PRIu64 ")\n"
" --seed NUMBER PRNG and ck_rhs seed (default: 0)\n",
program_name, DEFAULT_OPERATIONS);
}
static void
print_measurements(const char *heading, const char *const *names,
struct measurement *measurements, size_t count)
{
size_t i;
double per_call;
printf("\n%s:\n", heading);
printf(" %-22s %12s %18s %14s\n", "operation", "calls",
"total cycles", "cycles/call");
for (i = 0; i < count; i++) {
per_call = measurements[i].calls == 0 ? 0.0 :
(double)measurements[i].cycles / measurements[i].calls;
printf(" %-22s %12" PRIu64 " %18" PRIu64 " %14.2f\n",
names[i], measurements[i].calls, measurements[i].cycles,
per_call);
}
}
static void
run_randomized_workload(ck_rhs_t *rhs, struct key_pool *pool,
uint64_t operations, uint64_t seed)
{
uint64_t current, random, selector;
random = seed;
for (current = 0; current < operations; current++) {
selector = random_next(&random);
if ((selector & UINT64_C(0x1ffff)) == 0) {
operation_batch_delete(rhs, pool);
} else if ((selector & UINT64_C(0x7ff)) == 1) {
operation_iteration(rhs, pool);
} else {
selector = (selector >> 17) % 100;
if (selector < 25)
operation_put(rhs, pool, &random);
else if (selector < 45)
operation_set(rhs, pool, &random);
else if (selector < 60)
operation_fas(rhs, pool, &random);
else if (selector < 80)
operation_get(rhs, pool, &random);
else
operation_remove(rhs, pool, &random);
}
validate_count(rhs, pool);
}
}
int
main(int argc, char **argv)
{
struct key_pool pool;
struct ck_rhs_stat stat;
ck_rhs_t rhs;
uint64_t operations, seed, workload_start;
uint64_t workload_cycles;
size_t final_keys;
size_t pool_capacity;
int i;
operations = DEFAULT_OPERATIONS;
seed = 0;
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
usage(stdout, argv[0]);
return EXIT_SUCCESS;
} else if (strcmp(argv[i], "--ops") == 0) {
if (++i == argc) {
usage(stderr, argv[0]);
return EXIT_FAILURE;
}
operations = parse_u64("--ops", argv[i]);
} else if (strcmp(argv[i], "--seed") == 0) {
if (++i == argc) {
usage(stderr, argv[0]);
return EXIT_FAILURE;
}
seed = parse_u64("--seed", argv[i]);
} else {
fprintf(stderr, "Unknown option: %s\n", argv[i]);
usage(stderr, argv[0]);
return EXIT_FAILURE;
}
}
if (operations > SIZE_MAX - 1 ||
operations + 1 > SIZE_MAX / sizeof(*pool.slots) ||
operations + 1 > SIZE_MAX / sizeof(*pool.active)) {
benchmark_abort("--ops is too large for the key pool");
}
pool_capacity = (size_t)operations + 1;
rhs_seed = mix64(seed ^ UINT64_C(0xa0761d6478bd642f));
/*
* The instrumented pass provides operation and allocator breakdowns.
* A second, uninstrumented pass provides an end-to-end workload number
* that is not dominated by per-call timestamp overhead.
*/
measurement_enabled = true;
allocation_tracking_enabled = true;
pool_init(&pool, pool_capacity);
if (timed_rhs_init(&rhs, INITIAL_CAPACITY) == false)
benchmark_abort("ck_rhs_init failed");
run_randomized_workload(&rhs, &pool, operations, seed);
validate_all(&rhs, &pool);
ck_rhs_stat(&rhs, &stat);
final_keys = pool.active_count;
timed_rhs_destroy(&rhs);
pool_destroy(&pool);
measurement_enabled = false;
allocation_tracking_enabled = false;
pool_init(&pool, pool_capacity);
if (timed_rhs_init(&rhs, INITIAL_CAPACITY) == false)
benchmark_abort("ck_rhs_init failed during uninstrumented pass");
workload_start = benchmark_timestamp();
run_randomized_workload(&rhs, &pool, operations, seed);
workload_cycles = benchmark_timestamp() - workload_start;
validate_all(&rhs, &pool);
timed_rhs_destroy(&rhs);
pool_destroy(&pool);
printf("ck_rhs mixed benchmark\n");
printf("seed=%" PRIu64 " ops=%" PRIu64 " initial_capacity=%u "
"final_keys=%zu probe_maximum=%u\n", seed, operations,
INITIAL_CAPACITY, final_keys, stat.probe_maximum);
printf("mixed_workload_cycles=%" PRIu64 " cycles/op=%.2f\n",
workload_cycles, operations == 0 ? 0.0 :
(double)workload_cycles / operations);
print_measurements("Randomized operations", random_names,
random_measurements, RANDOM_OPERATIONS);
print_measurements("Meta operations", meta_names, meta_measurements,
META_OPERATIONS);
print_measurements("ck_rhs API calls", api_names, api_measurements,
API_OPERATIONS);
printf("\nAllocator:\n");
printf(" requests=%" PRIu64 " requested_bytes=%" PRIu64 "\n",
allocation_metrics.requests, allocation_metrics.requested_bytes);
printf(" frees=%" PRIu64 " freed_bytes=%" PRIu64 "\n",
allocation_metrics.frees, allocation_metrics.freed_bytes);
printf(" peak_live_bytes=%" PRIu64 " final_live_bytes=%" PRIu64 "\n",
allocation_metrics.peak_live_bytes, allocation_metrics.live_bytes);
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment