Foundry/examples/bench_hybridmem.c

351 lines
11 KiB
C

/* SPDX-License-Identifier: AGPL-3.0-or-later */
/* Copyright (C) 2026 David (Corey) Herrera */
/*
* HybridMem Integration Benchmark
*
* Proves the full pipeline works end-to-end with realistic dimensions:
* 1. Similarity preservation — HDC retrieval finds semantically similar entries
* 2. Persistence roundtrip — flush/reopen preserves correct retrieval
* 3. Performance — actual latency numbers at scale
*/
#include "../tensor/tensor.h"
#include "../hdc/hdc.h"
#include "../hybridmem/hybridmem.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#define EMBED_DIM 768
#define HDC_DIM 4096
#define N_TOKENS 16
#define N_LAYERS 4
#define BENCH_DIR "/tmp/bench_hybridmem"
/* ============================================================= */
/* Helpers */
/* ============================================================= */
static void rm_rf(const char *path) {
DIR *d = opendir(path);
if (!d) { unlink(path); return; }
struct dirent *entry;
while ((entry = readdir(d)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
char full[600];
snprintf(full, sizeof(full), "%s/%s", path, entry->d_name);
struct stat st;
if (stat(full, &st) == 0 && S_ISDIR(st.st_mode)) rm_rf(full);
else unlink(full);
}
closedir(d);
rmdir(path);
}
static double now_ms(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000.0 + ts.tv_nsec / 1e6;
}
/* Create [n_tokens, embed_dim] K/V tensor from a 1D embedding by tiling + noise */
static tm_tensor* tile_with_noise(tm_tensor *emb_1d, int n_tokens) {
int64_t shape[2] = { n_tokens, EMBED_DIM };
tm_tensor *t = tm_zeros(shape, 2, TM_F32);
if (!t) return NULL;
float *dst = (float *)t->data;
const float *src = (const float *)emb_1d->data;
for (int tok = 0; tok < n_tokens; tok++) {
for (int d = 0; d < EMBED_DIM; d++) {
/* Tile the embedding with slight per-token variation */
dst[tok * EMBED_DIM + d] = src[d] + 0.01f * (float)(tok * 7 + d * 3) / (float)EMBED_DIM;
}
}
return t;
}
/* Add small Gaussian-like noise to a 1D tensor */
static tm_tensor* add_noise(tm_tensor *emb_1d, float scale, int noise_seed) {
tm_tensor *noisy = tm_clone(emb_1d);
if (!noisy) return NULL;
float *d = (float *)noisy->data;
/* Simple deterministic pseudo-noise */
uint64_t state = (uint64_t)noise_seed + 7919;
for (int i = 0; i < EMBED_DIM; i++) {
state ^= state << 13; state ^= state >> 7; state ^= state << 17;
float noise = ((float)(state & 0xFFFF) / 32768.0f - 1.0f) * scale;
d[i] += noise;
}
return noisy;
}
/* ============================================================= */
/* Test 1: Similarity Preservation */
/* ============================================================= */
static int test_similarity_preservation(void) {
printf("--- Test 1: Similarity Preservation ---\n");
rm_rf(BENCH_DIR);
fm_config cfg = fm_config_default(EMBED_DIM);
cfg.store_dir = BENCH_DIR;
cfg.hdc_dim = HDC_DIM;
fm_store *store = fm_store_new(&cfg);
if (!store) { printf(" FAIL: could not create store\n"); return 1; }
int n_background = 1000;
int n_trials = 50;
/* Seed tensor RNG */
tm_manual_seed(42);
/* Store background entries (random noise) */
int64_t emb_shape[1] = { EMBED_DIM };
for (int i = 0; i < n_background; i++) {
tm_tensor *emb = tm_randn(emb_shape, 1, TM_F32);
tm_tensor *k = tile_with_noise(emb, N_TOKENS);
tm_tensor *v = tile_with_noise(emb, N_TOKENS);
fm_put(store, k, v, i % N_LAYERS, 1.0f);
tm_free(emb);
tm_free(k);
tm_free(v);
}
printf(" Stored %d background entries across %d layers\n", n_background, N_LAYERS);
/* Run needle queries */
int recall_at_1 = 0;
int recall_at_5 = 0;
int total_rank = 0;
printf(" Running %d needle queries...\n", n_trials);
for (int trial = 0; trial < n_trials; trial++) {
int layer = trial % N_LAYERS;
/* Create needle embedding */
tm_tensor *needle_emb = tm_randn(emb_shape, 1, TM_F32);
tm_tensor *needle_k = tile_with_noise(needle_emb, N_TOKENS);
tm_tensor *needle_v = tile_with_noise(needle_emb, N_TOKENS);
uint64_t needle_id = fm_put(store, needle_k, needle_v, layer, 1.0f);
/* Query with noisy version of needle */
tm_tensor *query = add_noise(needle_emb, 0.1f, trial);
fm_results *results = fm_query(store, query, layer, 5);
/* Check if needle is in results */
int found_rank = -1;
if (results) {
for (int i = 0; i < results->count; i++) {
if (results->items[i].entry_id == needle_id) {
found_rank = i + 1;
break;
}
}
}
if (found_rank == 1) recall_at_1++;
if (found_rank >= 1) recall_at_5++;
if (found_rank >= 1) total_rank += found_rank;
else total_rank += 6; /* penalty for miss */
fm_results_free(results);
tm_free(needle_emb);
tm_free(needle_k);
tm_free(needle_v);
tm_free(query);
}
float r1 = (float)recall_at_1 / n_trials;
float r5 = (float)recall_at_5 / n_trials;
float mean_rank = (float)total_rank / n_trials;
printf(" recall@1: %.2f recall@5: %.2f mean_rank: %.2f\n", r1, r5, mean_rank);
fm_store_free(store);
rm_rf(BENCH_DIR);
int pass = r5 >= 0.80f;
printf(" %s\n\n", pass ? "PASS" : "FAIL");
return pass ? 0 : 1;
}
/* ============================================================= */
/* Test 2: Persistence Roundtrip */
/* ============================================================= */
static int test_persistence_roundtrip(void) {
printf("--- Test 2: Persistence Roundtrip ---\n");
rm_rf(BENCH_DIR);
fm_config cfg = fm_config_default(EMBED_DIM);
cfg.store_dir = BENCH_DIR;
cfg.hdc_dim = HDC_DIM;
fm_store *store = fm_store_new(&cfg);
if (!store) { printf(" FAIL: could not create store\n"); return 1; }
tm_manual_seed(123);
int64_t emb_shape[1] = { EMBED_DIM };
int n_entries = 100;
int n_queries = 20;
/* Store entries and save query vectors */
uint64_t entry_ids[100];
tm_tensor *query_vecs[20];
int query_layers[20];
for (int i = 0; i < n_entries; i++) {
tm_tensor *emb = tm_randn(emb_shape, 1, TM_F32);
tm_tensor *k = tile_with_noise(emb, N_TOKENS);
tm_tensor *v = tile_with_noise(emb, N_TOKENS);
entry_ids[i] = fm_put(store, k, v, i % N_LAYERS, 1.0f);
/* Save some as queries */
if (i < n_queries) {
query_vecs[i] = tm_clone(emb);
query_layers[i] = i % N_LAYERS;
}
tm_free(emb);
tm_free(k);
tm_free(v);
}
/* Query before flush to get expected results */
uint64_t expected_top1[20];
for (int i = 0; i < n_queries; i++) {
fm_results *r = fm_query(store, query_vecs[i], query_layers[i], 1);
expected_top1[i] = (r && r->count > 0) ? r->items[0].entry_id : 0;
fm_results_free(r);
}
/* Flush and close */
fm_store_flush(store);
fm_store_free(store);
printf(" Stored %d entries, flushed to disk\n", n_entries);
/* Reopen */
fm_store *store2 = fm_store_open(BENCH_DIR);
if (!store2) {
printf(" FAIL: could not reopen store\n");
for (int i = 0; i < n_queries; i++) tm_free(query_vecs[i]);
rm_rf(BENCH_DIR);
return 1;
}
printf(" Reopened store, running %d queries...\n", n_queries);
int matched = 0;
for (int i = 0; i < n_queries; i++) {
fm_results *r = fm_query(store2, query_vecs[i], query_layers[i], 1);
uint64_t got = (r && r->count > 0) ? r->items[0].entry_id : 0;
if (got == expected_top1[i]) matched++;
fm_results_free(r);
tm_free(query_vecs[i]);
}
fm_store_free(store2);
rm_rf(BENCH_DIR);
printf(" %d/%d queries matched\n", matched, n_queries);
int pass = matched == n_queries;
printf(" %s\n\n", pass ? "PASS" : "FAIL");
return pass ? 0 : 1;
}
/* ============================================================= */
/* Test 3: Performance */
/* ============================================================= */
static int test_performance(void) {
printf("--- Test 3: Performance ---\n");
int scales[] = { 100, 500, 1000 };
int n_scales = 3;
int n_ops = 100;
tm_manual_seed(456);
int64_t emb_shape[1] = { EMBED_DIM };
for (int si = 0; si < n_scales; si++) {
int N = scales[si];
rm_rf(BENCH_DIR);
fm_config cfg = fm_config_default(EMBED_DIM);
cfg.store_dir = BENCH_DIR;
cfg.hdc_dim = HDC_DIM;
fm_store *store = fm_store_new(&cfg);
if (!store) continue;
/* Pre-populate */
for (int i = 0; i < N; i++) {
tm_tensor *emb = tm_randn(emb_shape, 1, TM_F32);
tm_tensor *k = tile_with_noise(emb, N_TOKENS);
tm_tensor *v = tile_with_noise(emb, N_TOKENS);
fm_put(store, k, v, i % N_LAYERS, 1.0f);
tm_free(emb);
tm_free(k);
tm_free(v);
}
/* Benchmark puts */
double t0 = now_ms();
for (int i = 0; i < n_ops; i++) {
tm_tensor *emb = tm_randn(emb_shape, 1, TM_F32);
tm_tensor *k = tile_with_noise(emb, N_TOKENS);
tm_tensor *v = tile_with_noise(emb, N_TOKENS);
fm_put(store, k, v, i % N_LAYERS, 1.0f);
tm_free(emb);
tm_free(k);
tm_free(v);
}
double put_ms = (now_ms() - t0) / n_ops;
/* Benchmark queries */
t0 = now_ms();
for (int i = 0; i < n_ops; i++) {
tm_tensor *q = tm_randn(emb_shape, 1, TM_F32);
fm_results *r = fm_query(store, q, i % N_LAYERS, 5);
fm_results_free(r);
tm_free(q);
}
double query_ms = (now_ms() - t0) / n_ops;
double qps = 1000.0 / query_ms;
printf(" N=%-6d put: %.1fms query: %.1fms (%.0f q/s)\n",
N + n_ops, put_ms, query_ms, qps);
fm_store_free(store);
rm_rf(BENCH_DIR);
}
printf(" PASS\n\n");
return 0;
}
/* ============================================================= */
/* Main */
/* ============================================================= */
int main(void) {
printf("\n=== HybridMem Integration Benchmark ===\n");
printf("embed_dim=%d hdc_dim=%d n_tokens=%d layers=%d\n\n",
EMBED_DIM, HDC_DIM, N_TOKENS, N_LAYERS);
int failures = 0;
failures += test_similarity_preservation();
failures += test_persistence_roundtrip();
failures += test_performance();
if (failures == 0) {
printf("=== ALL TESTS PASSED ===\n\n");
} else {
printf("=== %d TEST(S) FAILED ===\n\n", failures);
}
return failures > 0 ? 1 : 0;
}