Repository navigation
perf(symqg): complete degrees with worker-local reverse candidates - #156
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
SymphonyQG currently reserves up to 300 completion candidates per vertex and retains discarded edges across both pruning stages. Record candidates only during final reverse-edge pruning and complete each row immediately with reusable worker-local buffers, removing the global per-vertex candidate array and its reservations.
Reverse candidate collection still finishes before rows are modified, and graph encoding remains deferred until all rows are complete. Keep the existing candidate cap/order, thresholds and per-row fallback seeds. Add regression coverage for final/nonfinal completion, delayed graph writes, candidate recording and scratch reuse; simplify the two implementation-coupled test fixtures identified in review. Public API signatures and saved-index formats are unchanged.
Candidate provenance intentionally changes: search-pruned edges no longer participate in completion. This is a quality tradeoff relative to
main, not a lossless refactor. Fusion equivalence checks compare against an already reverse-only baseline.Testing
RABITQ_ENABLE_NATIVE_OPTIMIZATION=OFF), followed byctest --output-on-failure: 408 passed, 1 conditional skip; the separateOpenMPBindingtest passed.python -m pytest tests/python/test_symqg.py tests/python/test_qgkmeans.py -q: 153 passed../scripts/check-format.sh,./scripts/check-python.sh,git diff --check: passed../scripts/check-tidy.shusing a compilation-database subset forsrc/index/qg_builder.cpp: passed. The initial declaration/definition parameter-name diagnostic was fixed.Measurements and quality limits
Two separate local comparisons isolated the two parts of the change. Both used full SIFT1M/GIST1M, raw L2 storage, degree 32, construction ef 200, graph/builder seed 42, PiPNN seed 555, three alternating build pairs, a Xeon Gold 6418H, Clang 23.1.4 Release/native tuning OFF, and 48 logical build threads bound to NUMA node 0. The host was shared.
With reverse-only provenance held fixed, moving to worker-local completion produced:
Build time includes initialization/refinement/cleanup and excludes input loading and index saving. RSS is the whole-child high-water mark, including loading/saving; reducing refinement storage does not necessarily lower the overall peak. No phase-resolved peak-memory claim is made.
Separately, changing both-stage recording to reverse-only recording, while retaining global storage on both sides, measured the following at search ef 64 (Recall@10, supplied GT, all 10,000 SIFT / 1,000 GIST queries, one query thread on CPU/memory node 0):
Across the fixed search-ef grid 16–1024, median paired raw Recall changes ranged from +0.006 to +0.730 percentage points on SIFT and −0.650 to +0.160 on GIST. These observations preserve the quality regressions and are not an end-to-end paired measurement of the combined PR. Independent parallel graph builds can differ; no universal speedup or Recall-preservation claim is made.
Raw RSS/build observations for the fusion-only comparison
[ { "dataset": "sift", "build_seconds_before": [ 10.383282199, 10.358223093, 10.333064319 ], "build_seconds_after": [ 9.542925938, 9.558814312, 9.524627338 ], "max_rss_kib_before": [ 4507912, 4510328, 4508800 ], "max_rss_kib_after": [ 2307744, 2306700, 2298432 ] }, { "dataset": "gist", "build_seconds_before": [ 33.642027709, 33.685617752, 34.055308525 ], "build_seconds_after": [ 32.907826994, 32.842064494, 32.868561347 ], "max_rss_kib_before": [ 12252208, 12243804, 12257892 ], "max_rss_kib_after": [ 12231808, 12227168, 12229648 ] } ]Each entry follows pair order; relative changes are medians of paired ratios. Generated indexes were hashed and removed after measurement.