Skip to content

perf(diskann): reduce search scratch allocations - #101

Merged
huanglune merged 1 commit into
AlayaDB-AI:mainfrom
huanglune:perf/diskann-search-scratch
Jun 29, 2026
Merged

perf(diskann): reduce search scratch allocations#101
huanglune merged 1 commit into
AlayaDB-AI:mainfrom
huanglune:perf/diskann-search-scratch

Conversation

@huanglune

@huanglune huanglune commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Replace per-query DiskANN unordered_set / unordered_map state with reusable flat scratch buffers for visited bits, exact distances, cached neighbors, and in-flight I/O slots.
  • Reuse PQ query residual scratch and integrate neighbor-cache release with bounded frontier eviction to avoid hot-path allocations.
  • Size and resize scratch state through DiskANNLoadParams, with focused DiskANN tests covering the new scratch contract.

Test plan

  • git diff --check
  • cmake -B build/Release -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON
  • cmake --build build/Release --target test_diskann_pq test_diskann_beam_search test_diskann_tombstone_search -j$(nproc)
  • timeout 60s ctest --test-dir build/Release -R 'test_diskann_(pq|beam_search|tombstone_search)' --output-on-failure -j$(nproc)
  • uvx pre-commit run --files include/index/graph/diskann/beam_search.hpp include/index/graph/diskann/diskann_index.hpp include/index/graph/diskann/pq_table.hpp include/index/graph/diskann/search_scratch.hpp include/index/graph/diskann/visited_bitset.hpp include/index/graph/vamana/robust_prune.hpp tests/diskann/test_diskann_beam_search.cpp tests/diskann/test_diskann_pq.cpp tests/diskann/test_diskann_tombstone_search.cpp

Copilot AI review requested due to automatic review settings June 26, 2026 03:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request optimizes the DiskANN search path by replacing dynamic structures (such as std::unordered_set and std::unordered_map) with flat, pre-allocated structures like VisitedBitset and contiguous arrays in ThreadData. It also updates NeighborPriorityQueue to return insertion results and allows PQTable::preprocess_query to use a pre-allocated scratch buffer, reducing heap allocations. The review feedback focuses on further optimizing and cleaning up the new ThreadData implementation: specifically, using the standard std::isnan instead of manual bit-manipulation, tracking the in-flight count to make has_inflight() an O(1) operation, and optimizing the linear scan in remove_inflight() to avoid hot-path performance bottlenecks.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +155 to +161
[[nodiscard]] static bool is_missing_exact(float value) {
static constexpr uint32_t kExponentMask = 0x7F800000u;
static constexpr uint32_t kMantissaMask = 0x007FFFFFu;
uint32_t bits = 0;
std::memcpy(&bits, &value, sizeof(bits));
return (bits & kExponentMask) == kExponentMask && (bits & kMantissaMask) != 0;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This manual implementation to check for NaN is correct, but std::isnan() from the <cmath> header is more idiomatic, readable, and portable. Consider using it for better maintainability.

You would need to add #include <cmath> at the top of the file.

  [[nodiscard]] static bool is_missing_exact(float value) {
    return std::isnan(value);
  }

Comment on lines +213 to +217
[[nodiscard]] bool has_inflight() const {
return std::any_of(inflight.begin(), inflight.end(), [](const InFlightSlot &slot) {
return slot.occupied;
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The has_inflight() function performs a linear scan over the inflight vector, which can have up to 1024 elements. This can be optimized to an O(1) operation by maintaining a counter for the number of in-flight requests.

To implement this, you could:

  1. Add uint32_t inflight_count = 0; to the ThreadData struct.
  2. Increment this counter in set_inflight when a slot becomes occupied.
  3. Decrement it in remove_inflight when a request is successfully removed.
  4. Reset it to 0 in clear_inflight.
  5. Change has_inflight to use the counter as suggested.
  [[nodiscard]] bool has_inflight() const {
    return inflight_count > 0;
  }

Comment on lines +219 to +229
[[nodiscard]] bool remove_inflight(uint32_t id, InFlightSlot &out) {
for (InFlightSlot &slot : inflight) {
if (!slot.occupied || slot.id != id) {
continue;
}
out = slot;
slot.occupied = false;
return true;
}
return false;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

remove_inflight performs a linear scan over the inflight vector. Since this vector can have up to 1024 elements, this O(N) operation (where N is the number of page slots) could be a performance bottleneck in a hot path. The previous std::unordered_map implementation was O(1) on average.

To optimize this while avoiding heap allocations, you could maintain a separate small data structure that maps an id to a page_slot for in-flight requests. For example, a std::vector<std::pair<uint32_t, uint64_t>> kept sorted by id would allow for O(log k) lookups via binary search, where k is the number of in-flight requests. This would be a significant improvement over the current linear scan.

@huanglune
huanglune merged commit e77368c into AlayaDB-AI:main Jun 29, 2026
13 checks passed
@huanglune
huanglune deleted the perf/diskann-search-scratch branch July 19, 2026 11:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants