Skip to content

[CPP Runtime] Refactor storage tags and add static Vamana index support#297

Open
rfsaliev wants to merge 6 commits intomainfrom
rfsaliev/cpp-runtime-static-vamana
Open

[CPP Runtime] Refactor storage tags and add static Vamana index support#297
rfsaliev wants to merge 6 commits intomainfrom
rfsaliev/cpp-runtime-static-vamana

Conversation

@rfsaliev
Copy link
Member

Enable static Vamana index functionality support in CPP Runtime API.

This PR includes following changes:

  1. Refactored CPP Runtime storage factory utils to support configurable storage allocator.
  • StorageKindTag<Kind> is removed and replaced with StorageType<Kind, Alloc> in usages
  • Storagefactory::init() block_size argument is replaced with allocator - to be constructed in index implementation code (with appropriate block size)
  1. [static] VamanaIndex interface implementation.
  2. Refactored IVF storage factory utils to match changes made in step 1.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the C++ runtime storage/tag dispatch to be allocator-aware and introduces a concrete “static” VamanaIndex runtime API/implementation (plus LeanVec-specialized builders), updating existing IVF/Flat/Dynamic-Vamana code to the new storage plumbing and extending the C++ runtime test suite accordingly.

Changes:

  • Refactor runtime storage dispatch from StorageKindTag to StorageType<Kind, Alloc> and pass allocators into storage factories.
  • Add static VamanaIndex implementation (build/load/save/search/range_search) and LeanVec build specializations.
  • Update IVF/Flat/Dynamic-Vamana implementations + add runtime tests for static Vamana variants.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
include/svs/core/data/simple.h Adds is_blocked_v trait to detect blocked allocator wrappers.
bindings/cpp/src/svs_runtime_utils.h Reworks storage type mapping/factory APIs to be allocator-aware and tagless.
bindings/cpp/src/vamana_index_impl.h New static Vamana implementation (including LeanVec specialization).
bindings/cpp/src/vamana_index.cpp Implements the new VamanaIndex/VamanaIndexLeanVec runtime interfaces.
bindings/cpp/include/svs/runtime/vamana_index.h Expands VamanaIndex public API for static Vamana and LeanVec builders.
bindings/cpp/include/svs/runtime/dynamic_vamana_index.h Adapts DynamicVamana to the updated VamanaIndex base interface.
bindings/cpp/src/dynamic_vamana_index_impl.h Updates storage dispatch/build path for allocator-based storage factory.
bindings/cpp/src/dynamic_vamana_index_leanvec_impl.h Updates LeanVec tag dispatch to StorageType<Kind, Alloc>.
bindings/cpp/src/ivf_index_impl.h Refactors IVF storage dispatch to the new StorageType<Kind, Alloc> approach.
bindings/cpp/src/dynamic_ivf_index_impl.h Refactors Dynamic-IVF storage dispatch similarly.
bindings/cpp/src/flat_index_impl.h Updates Flat index storage type/tag usage to StorageType<Kind, Alloc>.
bindings/cpp/tests/runtime_test.cpp Adds static Vamana read/write/search/range-search tests and LeanVec training-path test.
bindings/cpp/CMakeLists.txt Gates link_mkl_static() behind SVS_EXPERIMENTAL_LINK_STATIC_MKL.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

@rfsaliev rfsaliev requested a review from Copilot March 23, 2026 18:03
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.

Comment on lines +162 to +164
assert(
blocksize_bytes.raw() > 0 && "Blocked storage types require a non-zero blocksize"
);
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

Relying on assert for validating blocksize_bytes means invalid input can slip through in release builds, potentially creating an allocator with an invalid block size and causing hard-to-debug failures later. Consider replacing the assert with a StatusException(ErrorCode::INVALID_ARGUMENT, ...) (or equivalent) so the runtime API returns a deterministic error instead of depending on debug-only checks.

Suggested change
assert(
blocksize_bytes.raw() > 0 && "Blocked storage types require a non-zero blocksize"
);
if (blocksize_bytes.raw() <= 0) {
throw svs::lib::StatusException(
svs::lib::ErrorCode::INVALID_ARGUMENT,
"Blocked storage types require a non-zero blocksize"
);
}
Copilot uses AI. Check for mistakes.
svs::runtime::v0::StorageKind::LeanVec4x4
)
.ok()) {
CATCH_REQUIRE(!status.ok());
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

The CATCH_REQUIRE(!status.ok()) assertion is unrelated to the LeanVec CPU support check (it asserts the training-data build failed whenever the storage kind is unsupported). Building LeanVecTrainingData can succeed even if the CPU doesn’t support the storage kind, so this can produce a false failure. Drop that assertion and just CATCH_SKIP(...) (optionally cleaning up training_data if it was created).

Suggested change
CATCH_REQUIRE(!status.ok());
if (training_data != nullptr) {
svs::runtime::v0::LeanVecTrainingData::destroy(training_data);
training_data = nullptr;
}
Copilot uses AI. Check for mistakes.
@rfsaliev rfsaliev force-pushed the rfsaliev/cpp-runtime-static-vamana branch from da6d148 to 1a773d7 Compare March 25, 2026 14:27
@rfsaliev rfsaliev requested a review from Copilot March 25, 2026 14:28
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.

Comment on lines 52 to +55
LeanVecTrainingDataImpl(
const svs::data::ConstSimpleDataView<float>& data, size_t leanvec_dims
)
: leanvec_dims_{leanvec_dims}
, leanvec_matrices_{compute_leanvec_matrices(data, leanvec_dims)} {}
: LeanVecTrainingDataImpl(compute_leanvec_matrices(data, leanvec_dims)) {}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

The lvq_leanvec_enabled() check happens only in the LeanVecTrainingDataImpl(LeanVecMatricesType&&) constructor, but the (data, leanvec_dims) constructor computes matrices before delegating to it. On CPUs without LeanVec support this can still execute unsupported instructions during compute_leanvec_matrices(...) and crash before the NOT_IMPLEMENTED error is thrown. Add the CPU-support check before calling compute_leanvec_matrices(...) (or inside compute_leanvec_matrices(...) itself) so unsupported CPUs fail safely.

Copilot uses AI. Check for mistakes.
Comment on lines 57 to +63
LeanVecTrainingDataImpl(
const svs::data::ConstSimpleDataView<float>& data,
const svs::data::ConstSimpleDataView<float>& queries,
size_t leanvec_dims
)
: leanvec_dims_{leanvec_dims}
, leanvec_matrices_{compute_leanvec_matrices_ood(data, queries, leanvec_dims)} {}
: LeanVecTrainingDataImpl(compute_leanvec_matrices_ood(data, queries, leanvec_dims)
) {}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

Same issue as above for the OOD constructor: it computes LeanVec matrices before the lvq_leanvec_enabled() guard runs (via delegation). This can crash on unsupported CPUs before returning a NOT_IMPLEMENTED status. Perform the CPU capability check before invoking compute_leanvec_matrices_ood(...) (or guard inside that function).

Copilot uses AI. Check for mistakes.
Comment on lines +162 to +166
if (blocksize_bytes.raw() <= 0) {
throw StatusException(
ErrorCode::INVALID_ARGUMENT,
"Blocked storage types require a non-zero blocksize"
);
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

make_allocator(PowerOfTwo) checks blocksize_bytes.raw() <= 0, but raw() is a size_t exponent so the only meaningful check here is == 0. Using == 0 avoids signed/unsigned confusion and makes it clear that the intent is to reject the default-constructed exponent.

Copilot uses AI. Check for mistakes.
Comment on lines +121 to +127
// Selective search with IDSelector
auto old_sp = get_impl()->get_search_parameters();
auto sp_restore = svs::lib::make_scope_guard([&]() noexcept {
get_impl()->set_search_parameters(old_sp);
});
get_impl()->set_search_parameters(sp);

Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

In the filtered-search path, this temporarily mutates the underlying index’s search parameters (set_search_parameters) inside a const method. This is not thread-safe if the same index instance is searched concurrently (different calls can race and restore the wrong parameters). Prefer an API that passes search parameters directly to the iterator/search, or protect the mutation with synchronization (or clearly document that concurrent searches are unsupported when using filters/params).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants