Repository navigation
bench(lite): align reproducible acceptance tools with Lite feature baseline - #2926
nw9663644-eng wants to merge 103 commits into
Conversation
Add an independent Lite shared-library target with FP32 squared-L2 CRUD, versioned snapshots, Catch2 tests, an installed consumer, and bilingual docs. Reuse the existing generic distance kernel and error contracts. Graph search, quantization, mmap, and Full format compatibility remain out of scope. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Run focused formatting, expanded clang-tidy, Debug tests, the 100k-vector workflow, Release dependency inspection, and an installed consumer in CI. Address the additional public-header and test lint findings. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Harden the growth edge case, make dense-slot invariants explicit, use a stable heap comparator, and provide an installed static Lite archive with CI and documentation coverage. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Use a const reference when serializing IDs and simplify signed ID decoding with an object-representation copy. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
|
/label status/waiting-for-review |
Merge Protections🔴 1 of 2 protections blocking · waiting on 🙋 you
🔴 Require kind labelWaiting for
This rule is failing.
Show 1 satisfied protection🟢 Require version label
|
LHT129
left a comment
There was a problem hiding this comment.
I have completed a thorough review of all 16 changed files (2,040 additions). The code quality is high across the board.
Summary of findings:
The existing inline comments from @LHT129 already cover the notable points:
src/lite/index.cpp:float valuecopy vsconst auto&consistency in Save looplite/benchmark/main.cpp:percentile()pass-by-value copies the vector on every calllite/benchmark/main.cpp:peak_rss_kib()usesgetrusage(RUSAGE_SELF)—ru_maxrssis in KiB on Linux but bytes on macOS (documented as Linux-only, so not a practical issue)lite/benchmark/run_baseline.sh:lscpuis Linux-specific (consistent with the documented Linux x86_64 scope)
Additional observations (no new inline comments needed):
-
The
make_vectorrefactoring in this commit (from XOR-combinedmixcalls to chainedmixcalls) improves clarity and matchesfull_main.cpp. Good change. -
src/lite/index.cpp— TheIndex::Loadfunction usesreturn std::move(*created);where the move is redundant (the compiler would move automatically from the dereferenced unique_ptr). This is harmless but unnecessary. -
lite/benchmark/main.cpp—run_stabilitycallspeak_rss_kib()per round, which reports the cumulative process-wideru_maxrss. For a stability experiment this is actually the desired behavior (it detects monotonic memory growth), so the semantics are correct for the use case. -
The snapshot format (
VSAGLT01) is well-designed with explicit little-endian byte encoding, bounds validation before allocation, and rejection of truncation, trailing bytes, duplicate IDs, and non-finite values. The test coverage for malformed snapshots is thorough. -
The
Addimplementation correctly uses geometric capacity growth and publishes the slot map entry only after all potentially-throwing vector operations succeed — this is proper exception safety. -
The CI workflow (
.github/workflows/lite.yml) correctly verifies thatlibvsag-lite.sodoes not link againstlibvsag.so, which is the key isolation guarantee.
No blocking issues found. The existing inline comments are all [note] or [suggestion] level and none should block merging.
| } | ||
| return neighbors; | ||
| } | ||
|
|
There was a problem hiding this comment.
[suggestion] The loop below iterates k times unconditionally, reading GetIds()[i] and GetDistances()[i] without verifying that the result actually contains k elements. Combined with the GetDim() check on line 151 (which validates the vector dimension, not the result count), this can read out-of-bounds if KnnSearch returns fewer than k results.
While the benchmark currently operates on indices that always have enough vectors to satisfy k, a future change or an unexpected internal error could trigger undefined behavior here.
Consider either:
- Fixing line 151 to use
GetNumElements()(as noted in the critical comment above), which would make this loop safe by failing early, or - Adding an explicit bounds check before the loop:
require(static_cast<uint64_t>((*result)->GetNumElements()) >= k, "too few search results");Current:
for (uint64_t i = 0; i < k; ++i) {
neighbors.push_back({(*result)->GetIds()[i], (*result)->GetDistances()[i]});
}| run(const std::string& directory, const std::string& snapshot) { | ||
| const auto base_path = directory + "/base.fvecs"; | ||
| const auto query_path = directory + "/queries.fvecs"; | ||
| const auto truth_path = directory + "/groundtruth.ivecs"; |
There was a problem hiding this comment.
[critical] The percentile function in dataset_main.cpp is missing the std::min guard present in the same function in main.cpp and full_main.cpp. When fraction=0 and values is non-empty, rank = ceil(0) - 1 = 0 - 1 underflows to UINT64_MAX, causing an out-of-bounds access on values[rank].
Compare with the safe version in main.cpp:
return values[std::min<uint64_t>(rank, values.size() - 1)];Current (dataset_main.cpp):
double
percentile(std::vector<double> values, double fraction) {
std::sort(values.begin(), values.end());
const auto rank =
static_cast<uint64_t>(std::ceil(fraction * static_cast<double>(values.size()))) - 1;
return values[rank];
}Suggested fix:
double
percentile(std::vector<double> values, double fraction) {
std::sort(values.begin(), values.end());
const auto rank =
static_cast<uint64_t>(std::ceil(fraction * static_cast<double>(values.size()))) - 1;
return values[std::min<uint64_t>(rank, values.size() - 1)];
}Note: fraction=0 is a valid input for P0 percentile, and while the current callers always pass 0.50 or 0.99, this is a latent bug that could be triggered by future changes or refactoring.
There was a problem hiding this comment.
Addressed in e37ea30. The current helpers already reject empty inputs/non-finite or out-of-range fractions and use max(1, ceil(f*n))-1, so P0 cannot underflow. Added the same upper clamp to all dataset variants for consistency. The new compiled-helper regression executes all five real implementations for P0/P50/P99/P100, singleton/empty input and invalid fractions; input samples remain unchanged. No historical measurements were recomputed.
| std::ifstream input(path, std::ios::binary | std::ios::ate); | ||
| require(static_cast<bool>(input), "input open failed"); | ||
| const auto size = input.tellg(); | ||
| require(size > 0 and size % (sizeof(int32_t) + sizeof(T) * expected_dim) == 0, |
There was a problem hiding this comment.
[suggestion] In read_records, const auto size = input.tellg() deduces size as std::streampos, which is a signed type. The subsequent size % (sizeof(int32_t) + sizeof(T) * expected_dim) performs a signed modulo with an unsigned operand, and size > 0 compares a signed value. While this works correctly on typical platforms for valid inputs, using an explicit static_cast<uint64_t> would make the intent clearer and avoid signed/unsigned comparison warnings.
Current:
const auto size = input.tellg();
require(size > 0 and size % (sizeof(int32_t) + sizeof(T) * expected_dim) == 0,
"invalid input file length");
const auto count = static_cast<uint64_t>(size / (sizeof(int32_t) + sizeof(T) * expected_dim));Suggested:
const auto size = static_cast<uint64_t>(input.tellg());
require(size > 0 and size % (sizeof(int32_t) + sizeof(T) * expected_dim) == 0,
"invalid input file length");
const auto count = size / (sizeof(int32_t) + sizeof(T) * expected_dim);There was a problem hiding this comment.
Fixed in e37ea30: reject non-positive tellg() and invalid dimensions before converting to uint64_t, then use unsigned record-byte arithmetic. Applied to both dataset consumers. Compiled-reader regressions cover valid rows, empty/truncated files and invalid dimensions.
Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Document current cached incoming/safe-link policy and map required outputs to public functionality, implementation, regressions and acceptance limits. Separate current bilingual contracts from pinned historical experiments. Exercise default-OFF, ON, sanitizer and coverage configurations in CI, including installed consumers, all configured Lite units/private headers, gcov line-union fixtures, a 90 percent gate and retained diagnostics. Fix the private header's existing modernize-use-auto diagnostic without changing the type or algorithm. No rejected graph policy is enabled. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Measure current PR2904 dbb2fe0 from the benchmark-only PR2926 branch. Record two datasets, three storages and three rotated fresh-process repeats, real-vector changes, interleaved deletion-aware queries, CPU/resource samples and per-round persistence checks. Preserve reproducible raw evidence and explicit final acceptance gaps. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Retain the measured PR2904 index across 20 rounds, pre-touch measurement storage, record before-persistence RSS and observed thread count. Add lifecycle parity and archive safety tests, preserve 18 fresh-process raw cohorts in a hash-verified archive, and report recall, latency and late-memory limitations without claiming hours-long acceptance. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Freshly compile consumers against current PR2904 and the hash-verified pinned native Full installation. Record two datasets, three storages, 36 builders and108 independent loaders at equal query ef512, plus raw archive, ELF footprint and independent audits. Preserve quality differences, incremental RSS and persistence costs; do not claim same-quality, fresh Full library or complete-package acceptance. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Package fresh native C++ consumers with recursive ELF dependencies, loader, launcher and license texts under a defined runtime profile. Verify relocation and package-local library maps for six storage smokes, record per-file accounting and real tar.gz bytes, and preserve exact native stdout. No production changes or certified-release/fresh-Full claims. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Build native Full core from an empty RAM build with ccache disabled, retain compilation and dependency boundaries, and audit a fresh equal-query-budget two-dataset matrix. Preserve negative results and distinguish this evidence from historical package and CRUD cohorts. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Calibrate fixed snapshots on 100 GIST queries, freeze budgets, and validate on 900 separate query rows. Preserve failed Lite quality gates, native Full comparisons, phase CPU/resource receipts, FP64 oracle agreement, and archived raw results. No production changes. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Trace the prior GIST snapshots without changing budgets or production code, reproduce all native neighbor IDs and distance bits, and retain disjoint routing/ranking loss evidence. Identify shared coverage gaps and distinguish the complete-code scan from a practical ANN quality result. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Preserve the source-backed nearest-selection candidate and paired SIFT/GIST results. Recall improves while build and query latency regress; no default adoption or complete acceptance claim. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Merge current upstream without rewriting published Lite commits. Compare coverage against the actual merge parent and validate the PR head. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
…e evidence Merge current PR2904 feature baseline 8e82a2a without changing supported production semantics. Keep optional acceptance tools default OFF, add audit guard CI tests, and provide requirement-to-source/report/raw traceability. Preserve all historical results at immutable 800a822 with per-file SHA256 manifest instead of including bulky raw output in the latest review tree. Separate historical measurements and candidate policies from delivered code. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
…ance Normally merge feature CI fixes and upstream ancestry. Preserve historical metric identities, publish fresh resident SIFT/GIST raw evidence, and retain unresolved quality/community gates. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Retain the formal acceptance dependency closure and record immutable paths and hashes for 32 archived tools/documents. Validate Lite ON/OFF, native Full consumers, SIFT preparation and raw replay without changing production code or performance claims. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Separate current, historical and rejected-candidate results; report CRUD, CPU, native Full, package, 100k and quality limits. Restore pinned Cohere preparation and rejection fixtures with optional-dependency skip documentation. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Separate experiment conditions, before/after tables, conclusions and limits. Preserve values and distinguish historical resident records from current measurements. No production changes or new experiments. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Remove development timeline, source identifiers and audit details from the reader report while retaining evidence links. Recheck all 18 resource records, show three-trial ranges and separate query CPU from whole-test costs. Do not cherry-pick single best runs or claim failed candidates as accepted. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Require the pinned Full receipt before output creation, cover CLI failures, convert import-time assertions into four unittest cases, install optional benchmark dependencies in CI with an import gate, and clarify retained raw evidence. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Validate stream positions before unsigned conversion, make percentile bounds consistent, add compiled helper regression cases and clarify native Full result dimensions. Rename file hashes consistently across dataset preparation. No Lite implementation or historical metric changes. Signed-off-by: Nannan Wang <1794949109@qq.com> Assisted-by: Codex:GPT-5
Purpose
Provide reproducible benchmarks and acceptance evidence for VSAG Lite.
Closes #2888. Depends on #2904, which owns the Lite implementation, API, architecture and functional tests.
Contents
Historical exploration tools are archived at pinned commits rather than included in the current review tree. Until #2904 merges, the main-target diff also includes its feature dependency.
Validation
These are recorded local results, not a claim that hosted CI has passed. Restoring Cohere tools did not rerun Cohere performance.
Results and Limits
Defined package profiles and some load/memory comparisons show clear benefits. RaBitQ is not consistently faster than FP16; GIST dynamic quality, a unified same-quality/SIMD-aligned Full comparison and long-duration 100k/Cohere validation remain open. Historical and candidate results are explicitly separated from current production results.
Report
Read the comprehensive acceptance report.
Review follow-up: stream-position validation, consistent percentile bounds, compiled helper regressions and file-hash naming were addressed. Native Full was smoke-tested with dimension 16 and k=3 to confirm its result-layout contract; 13 representative inline threads received evidence-based replies. Existing historical measurements and Lite implementation are unchanged. Other historical/duplicate comments have not all been resolved.