yams/tests/benchmarks/vector_memory_scaling_bench.cpp
Trevon 4e97b31fef
Some checks failed
Fuzz Smoke / libFuzzer smoke (push) Has been cancelled
chore(warnings): clean up compiler warnings on GCC, Clang and Apple Clang (#279)
* chore(warnings): default-initialize aggregate members left out of brace-init

GCC -Wmissing-field-initializers fired about 1300 times where tests and
src build structs with a short positional brace list. Give the omitted
members an empty default member initializer. Value-initialization from
{} is what aggregate init already did for them, so behavior is
unchanged.

Signed-off-by: trvon <me@trevon.dev>

* chore(warnings): remove or annotate unused parameters, functions and variables

- init_command, s3_signer test, request_handler_lifetime test: comment out
  the unused parameter name.
- video_content_handler: path is only read under YAMS_HAVE_FFPROBE.
- vector_memory_scaling_bench: main() takes no arguments.
- daemon_status_render: delete unused neutralText helper.
- DaemonMetrics: mallocStackLoggingNoCompactEnabled is only called in the
  __APPLE__ block, so define it there.
- SocketServer: einval_streak only matters in the __APPLE__ EINVAL path.
- onnx_plugin_harness test, vector_backend_engine_compare bench,
  work_coordinator test: mark platform-conditional or busy-loop
  variables [[maybe_unused]].

Signed-off-by: trvon <git@trevon.dev>

* chore(warnings): fix sign-compare, range-loop, nodiscard and dangling-ref in tests

- search_admission_controller: count started workers in std::uint32_t so
  the comparison with the uint32 cap is same-signed.
- p2p_manager: iterate the invalid-host list by const reference.
- integrity_verifier: discard the nodiscard results the tests do not
  inspect with static_cast<void>.
- post_ingest_queue: findNode/findEdge take std::string_view so no
  temporary std::string is created for the call (GCC
  -Wdangling-reference false positive on the returned reference).
- topology_ablation_bench: the doc comment ended a line with a backslash,
  which continues the comment (-Wcomment); show the env export on its own
  line.

Signed-off-by: trvon <git@trevon.dev>

* chore(warnings): drop redundant std::move of a const captured json

The coroutine lambda is not mutable, so the captured optional<json> is
const and std::move(*progressToken) already selected the copy assignment.
Drop the std::move so the code states what actually happens.

Signed-off-by: trvon <git@trevon.dev>

* chore(warnings): mark __int128 use in db_integrity_stamp as an extension

-Wpedantic flags __int128. Use a __extension__ typedef, the GCC/Clang
idiom for an intentional extension, instead of the bare keyword.

Signed-off-by: trvon <git@trevon.dev>

* chore(warnings): remove always-true unsigned >= 0 comparisons

GCC -Wtype-limits flagged 51 test assertions of the form
CHECK(unsignedValue >= 0) that can never fail, plus a dead
`totalCount.value() < 0` clause in the graph listByType handler (the
count is std::size_t). In the tests keep the evaluation as a
static_cast<void> so the accessors are still exercised; in the handler
drop the dead clause and keep the range check.

Signed-off-by: trvon <me@trevon.dev>

---------

Signed-off-by: trvon <me@trevon.dev>
Signed-off-by: trvon <git@trevon.dev>
2026-10-08 14:30:28 -06:00

121 lines
3.6 KiB
C++

#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <random>
#include <string>
#include <vector>
#include <yams/vector/sqlite_vec_backend.h>
using namespace yams::vector;
namespace {
struct Config {
size_t dim = 128;
size_t vectors = 1000;
unsigned seed = 42;
};
std::vector<float> makeRandomVec(size_t dim, std::mt19937& rng) {
std::uniform_real_distribution<float> dist(-1.0f, 1.0f);
std::vector<float> v(dim);
for (auto& x : v)
x = dist(rng);
return v;
}
void bench(const char* engineLabel, VectorSearchEngine engine, const Config& cfg) {
std::mt19937 rng(cfg.seed);
SqliteVecBackend::Config beCfg;
beCfg.embedding_dim = cfg.dim;
beCfg.search_engine = engine;
SqliteVecBackend backend(beCfg);
auto init = backend.initialize(":memory:");
if (!init) {
std::printf("%-14s d=%zu n=%zu ERROR: %s\n", engineLabel, cfg.dim, cfg.vectors,
init.error().message.c_str());
return;
}
auto create = backend.createTables(cfg.dim);
if (!create) {
std::printf("%-14s d=%zu n=%zu ERROR: %s\n", engineLabel, cfg.dim, cfg.vectors,
create.error().message.c_str());
return;
}
std::vector<VectorRecord> records;
records.reserve(cfg.vectors);
for (size_t i = 0; i < cfg.vectors; ++i) {
VectorRecord rec;
rec.chunk_id = "chunk_" + std::to_string(i);
rec.document_hash = "doc_" + std::to_string(i / 4);
rec.embedding = makeRandomVec(cfg.dim, rng);
rec.content = "benchmark content " + std::to_string(i);
records.push_back(std::move(rec));
}
auto t0 = std::chrono::steady_clock::now();
auto insert = backend.insertVectorsBatch(records);
if (!insert) {
std::printf("%-14s d=%zu n=%zu INSERT ERROR\n", engineLabel, cfg.dim, cfg.vectors);
return;
}
auto build = backend.buildIndex();
if (!build) {
std::printf("%-14s d=%zu n=%zu BUILD ERROR\n", engineLabel, cfg.dim, cfg.vectors);
return;
}
auto t1 = std::chrono::steady_clock::now();
// Run a few queries to get steady-state latency
std::vector<float> query = makeRandomVec(cfg.dim, rng);
const int queryCount = 20;
double totalUs = 0;
for (int q = 0; q < queryCount; ++q) {
auto qs = std::chrono::steady_clock::now();
auto result = backend.searchSimilar(query, 10, 0.0f);
auto qe = std::chrono::steady_clock::now();
if (!result)
continue;
totalUs += std::chrono::duration<double, std::micro>(qe - qs).count();
}
double meanUs = totalUs / queryCount;
double buildMs = std::chrono::duration<double, std::milli>(t1 - t0).count();
std::printf("%-14s d=%-4zu n=%-6zu build=%8.1f ms query=%8.1f us\n", engineLabel, cfg.dim,
cfg.vectors, buildMs, meanUs);
}
} // namespace
int main() {
std::printf("%-14s %-5s %-7s %-12s %-12s\n", "engine", "dim", "vectors", "build_ms",
"query_us");
std::printf("%s\n", std::string(56, '-').c_str());
// Sweep dims at a fixed corpus size
for (size_t dim : {128ul, 384ul, 1024ul}) {
Config cfg;
cfg.dim = dim;
cfg.vectors = 2000;
bench("simeon-pq", VectorSearchEngine::SimeonPqAdc, cfg);
bench("vec0-l2", VectorSearchEngine::Vec0L2, cfg);
}
// Sweep corpus sizes at a fixed dim
for (size_t n : {500ul, 2000ul, 10000ul}) {
Config cfg;
cfg.dim = 384;
cfg.vectors = n;
bench("simeon-pq", VectorSearchEngine::SimeonPqAdc, cfg);
bench("vec0-l2", VectorSearchEngine::Vec0L2, cfg);
}
return 0;
}