From 79bc90e512cd25dbd11c934fb8d241fa08211a42 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Sat, 26 Sep 2026 22:46:20 +0200 Subject: [PATCH] RotationIndexer: keep 1024 indexing outcomes, keyed by a digest of the input A run on data that index poorly asks over a hundred indexing questions - every rung of the spot-budget ladder, in every pass and walk probe - and a later pass repeats a probe's ladder, which the 32-entry memo had long evicted (about 20 s on one such sweep). The key holds every spot, so it is now kept as two independent 64-bit hashes and its length instead of whole. RUGNUX_VERIFY_FIRST_PASS_MEMO still recomputes and compares. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01D1G8gJVAy6gp1K5Dz3NE5C --- .../rotation_indexer/RotationIndexer.cpp | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/image_analysis/rotation_indexer/RotationIndexer.cpp b/image_analysis/rotation_indexer/RotationIndexer.cpp index 613766277..9b911ae3d 100644 --- a/image_analysis/rotation_indexer/RotationIndexer.cpp +++ b/image_analysis/rotation_indexer/RotationIndexer.cpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace { // Sub-cell override thresholds used in candidate selection to undo a spurious axis doubling: @@ -160,16 +161,32 @@ namespace { static_cast(x.GetIndexingThreads()), static_cast(x.GetRefineThreads())}); } + // The input key is kept as two independent 64-bit hashes of its bytes plus its length - a key + // holds every spot, so keeping it whole would cost megabytes per entry. + struct KeyDigest { + size_t n; + uint64_t fnv, std_hash; + bool operator==(const KeyDigest &) const = default; + }; + KeyDigest Digest(const std::vector &key) { + const std::string_view bytes(reinterpret_cast(key.data()), key.size() * sizeof(double)); + uint64_t fnv = 14695981039346656037ULL; // FNV-1a + for (const char c : bytes) + fnv = (fnv ^ static_cast(c)) * 1099511628211ULL; + return {key.size(), fnv, std::hash{}(bytes)}; + } + struct IndexingMemo { std::mutex m; - std::vector, RotationIndexer::IndexingOutcome>> entries; + std::vector> entries; }; IndexingMemo &Memo() { static IndexingMemo memo; return memo; } - // A run asks a handful of distinct questions at most; the oldest go first. - constexpr size_t MAX_INDEXING_MEMOS = 32; + // A run on data that index poorly asks well over a hundred questions (every rung of the spot-budget + // ladder, in every pass and probe), and a later pass repeats a probe's; the oldest go first. + constexpr size_t MAX_INDEXING_MEMOS = 1024; // Bitwise comparison of what a recomputation gave against what was kept. bool SameOutcome(const RotationIndexer::IndexingOutcome &a, const RotationIndexer::IndexingOutcome &b) { @@ -229,7 +246,7 @@ void RotationIndexer::RunIndexing() { if (!axis_) return; - const std::vector key = InputKey(); + const KeyDigest key = Digest(InputKey()); std::optional kept; { auto &memo = Memo();