Skip to content

Add Scalable Vector Search (SVS) library integration [MOD-8811] #598

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Apr 3, 2025

Conversation

rfsaliev
Copy link
Collaborator

@rfsaliev rfsaliev commented Feb 12, 2025

This PR introducing new index algorithm based on the Scalable Vector Search library.

Scalable Vector Search (SVS) is a performance library for vector similarity search based on Vamana indexing algorithm.
Thanks to the use of Locally-adaptive Vector Quantization (LVQ) and its highly optimized indexing and search algorithms,
SVS provides vector similarity search:

  • on billions of high-dimensional vectors,
  • at high accuracy
  • and state-of-the-art speed,
  • while enabling the use of less memory than its alternatives.

This enables application and framework developers using similarity search to unleash its performance on Intel ® Xeon CPUs (2nd generation and newer).

Full information about Scalable Vector Search functionalities and features described in the SVS documentation.

Change details

  • This PR introduces API changes
  • This PR introduces serialization changes

API changes

  • New algorithm kind 'VecSimAlgo_SVS`
  • New index construction parameters SVSParams
  • New index query runtime parameters SVSRuntimeParams

Build configuration changes

  • Added SVS library dependency
  • New CMake option SVS_REPOSITORY allows to specify Scalable Vector Search repository URL
  • Added compiler optimization options required to enable platform optimizations implemented in SVS

SVS library integration sources

  • svs.h - SVSIndex class to implement new algorithm
  • svs_batch_iterator.h - Batch Iterator implementation for SVSIndex
  • svs_utils.h - Utility classes and functions for 'SVS' algorithm implementation
  • svs_extensions.h - Optional LVQ support
  • svs_factory.h, svs_factory.cpp - Index factory utilities for the 'SVS' algorithm
  • test_svs.cpp - Unit tests for 'SVS' algorithm implementation

TODO

@CLAassistant
Copy link

CLAassistant commented Feb 12, 2025

CLA assistant check
All committers have signed the CLA.

@rfsaliev rfsaliev changed the base branch from main to feature_svs_index_support February 13, 2025 09:59
@rfsaliev rfsaliev marked this pull request as ready for review February 13, 2025 10:00
@rfsaliev rfsaliev force-pushed the scalable-vector-search branch from b0520e7 to 3193382 Compare February 13, 2025 10:58
@alonre24 alonre24 changed the title Add Scalable Vector Search (SVS) library integration Add Scalable Vector Search (SVS) library integration [MOD-8811] Feb 13, 2025
Copy link

codecov bot commented Feb 13, 2025

Codecov Report

Attention: Patch coverage is 88.63636% with 55 lines in your changes missing coverage. Please review.

Project coverage is 96.52%. Comparing base (bfa32c9) to head (b811141).
Report is 3 commits behind head on main.

Current head b811141 differs from pull request most recent head 48c5f7f

Please upload reports for the commit 48c5f7f to get more accurate results.

Files with missing lines Patch % Lines
src/VecSim/index_factories/svs_factory.cpp 63.91% 35 Missing ⚠️
src/VecSim/algorithms/svs/svs_utils.h 88.33% 7 Missing ⚠️
src/VecSim/algorithms/svs/svs.h 97.28% 6 Missing ⚠️
src/VecSim/algorithms/svs/svs_batch_iterator.h 91.22% 5 Missing ⚠️
src/VecSim/vec_sim.cpp 92.85% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #598      +/-   ##
==========================================
- Coverage   97.23%   96.52%   -0.71%     
==========================================
  Files         106      110       +4     
  Lines        5713     6194     +481     
==========================================
+ Hits         5555     5979     +424     
- Misses        158      215      +57     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rfsaliev rfsaliev force-pushed the scalable-vector-search branch from fb170d9 to 7b4483e Compare February 14, 2025 14:06
Copy link
Collaborator

@meiravgri meiravgri left a comment

Choose a reason for hiding this comment

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

This is very exciting!

For future development, please pay special attention to in-code comments that explain the logic and purpose behind key parts of the code.

Please note that any internal implementation of SVS dependency that uses threads should not be used.
Our library has its own thread pool, which should be used via RediSearch API to ensure proper resource management.
The same applies to logging—the SVS loger should point to the VecSimIndexInterface logging function

I also noticed a warning raised both in our CI and when building locally:

svs_extensions.h:57:17: note: '#pragma message: SVS LVQ is not available'

Is this expected?

Additionally, I observed that the SVS unit tests take significantly longer to run compared to their equivalent HNSW tests. Could you clarify why that might be?

auto prefetch_parameters =
svs::index::vamana::extensions::estimate_prefetch_parameters(data);
auto builder = svs::index::vamana::VamanaBuilder(
graph, data, std::move(distance), parameters, threadpool, prefetch_parameters);
Copy link
Collaborator

Choose a reason for hiding this comment

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

so actually the distance calculator component of the index is not going to be used?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

SVS has it's own distance calculation component with support of LVQ

Copy link
Collaborator

Choose a reason for hiding this comment

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

so why is it initialized in svs factory?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sorry, do not understand the question.

Copy link
Collaborator

@meiravgri meiravgri Apr 1, 2025

Choose a reason for hiding this comment

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

i'll open a pr to allow creating an index without a distance calculator component

@rfsaliev
Copy link
Collaborator Author

rfsaliev commented Mar 5, 2025

This is very exciting!

Thank you much @meiravgri for the review.

For future development, please pay special attention to in-code comments that explain the logic and purpose behind key parts of the code.

Sure, I will add comments in most questionable parts of code. I see that many questions in the review were related to SVS API limitations and requirements - so I will try to explain the logic behind.

Please note that any internal implementation of SVS dependency that uses threads should not be used. Our library has its own thread pool, which should be used via RediSearch API to ensure proper resource management. The same applies to logging—the SVS loger should point to the VecSimIndexInterface logging function

For the performance purpose, SVS utilizes kind of 'backend' parallelization which is based on internal threadpool. I agree that it would prefer to use RediSearch threadpool, but unfortunately, SVS TP usage do not match to RedisSearch behavior: RediSearch thread pool is seemed like 'asynchronous' - current thread is not blocked by tasks submitted to RediSearch TP. When SVS requires 'synchronous' threadpool - current thread should be blocked util all tasks are finished.
As far as you have much more knowledge of RediSearch thread pooling, I would appreciate if you help me to solve this conflict.
Thank you..

I also noticed a warning raised both in our CI and when building locally:

svs_extensions.h:57:17: note: '#pragma message: SVS LVQ is not available'

Is this expected?

Yes, it is kind of warning that LVQ feature is not enabled for the SVS version you got.

Additionally, I observed that the SVS unit tests take significantly longer to run compared to their equivalent HNSW tests. Could you clarify why that might be?
I did not compare unit tests performance of SVS and HNSW, but I can assume, that the main root cause can be in index initialization time: SVS is optimized for adding to index a batch of vectors when VecSim API is defined to add a vector-by-vector.
The 'tiered SVS index' feature (implemented there: rfsaliev#1) is intended to solve this issue.

Copy link
Collaborator

@alonre24 alonre24 left a comment

Choose a reason for hiding this comment

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

Nice work!
Added a few comments to continue @meiravgri's review.
Let's make sure that commented code is removed and TODOs are handled before we continue the review. Thanks!

@@ -48,6 +48,43 @@ static VecSimResolveCode _ResolveParams_EFRuntime(VecSimAlgo index_type, VecSimR
return VecSimParamResolver_OK;
}

static VecSimResolveCode _ResolveParams_WSSearch(VecSimAlgo index_type, VecSimRawParam rparam,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please add unit tests for errors use cases as well for coverage

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Unit tests added to 'test_svs.cpp'

Comment on lines 25 to 27
set(SVS_REPOSITORY "https://github.com/intel/ScalableVectorSearch.git" CACHE STRING "SVS repository")

include(FetchContent)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a particular reason to use FetchContent rather than using a submodule?

Copy link
Collaborator Author

@rfsaliev rfsaliev Mar 10, 2025

Choose a reason for hiding this comment

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

We just followed existing VecSim approach: here is no .gitmodules but googletest, google_benchmark, pybind11 linked using FetchContent

Copy link
Collaborator

Choose a reason for hiding this comment

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

As discussed, since svs is a substantial submodule that is not only required for testing, we will want to have it as a git submodule to have a more convenient mechanism for tracking and changing the versions.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've switched SVS to submodule, but got PR builds fail because submodule update is not run in CI builds.

Copy link
Collaborator

Choose a reason for hiding this comment

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

should be fixed now

@rfsaliev rfsaliev force-pushed the scalable-vector-search branch 3 times, most recently from 6555a2d to 6d8b83d Compare March 13, 2025 15:19
@rfsaliev rfsaliev requested review from alonre24 and meiravgri March 13, 2025 15:31
@rfsaliev
Copy link
Collaborator Author

@alonre24, @meiravgri, I've made most of requested changes except cases where I need your answers/decisions (see my comments).
Can you please review updated code and my responses to your comments?
Thank you.

Copy link
Collaborator

@meiravgri meiravgri left a comment

Choose a reason for hiding this comment

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

Great work, and well done on improving the comments!
I’ve mostly completed the review—batch iterator is still WIP, but you can start addressing the requested modifications.

My main concerns:

  • Redundant allocation of preprocessors and index calculators.

  • Duplicate preprocessing happening in both VecSim and SVS.

  • Thread safety—what ensures it in the current implementation? I didn’t see any locks. Correct me if I’m wrong, but we agreed that this phase would include global locks.

@rfsaliev rfsaliev force-pushed the scalable-vector-search branch 2 times, most recently from fb22f24 to 8e82d4d Compare March 28, 2025 13:30
@rfsaliev rfsaliev changed the base branch from feature_svs_index_support to main March 28, 2025 13:30
@rfsaliev rfsaliev force-pushed the scalable-vector-search branch 2 times, most recently from 0a19127 to feca261 Compare March 31, 2025 12:57
memcpy(processed_blob.get(), original_data, data_size);
// Preprocess each vector in place
for (size_t i = 0; i < n; i++) {
this->preprocessQueryInPlace(static_cast<DataType *>(processed_blob.get()) +
Copy link
Collaborator

Choose a reason for hiding this comment

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

correct. We should have an equivalent preprocessForStorageInPlace
that's on me to implement

}

int addVectorsImpl(const void *vectors_data, const labelType *labels, size_t n) {
if (n == 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

as discussed, assert n == 1 until tiered index is supported

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

n==1 is the normal case to implement VecSimIndexInterface see int addVector(const void *vector_data, labelType label) override at line 288

Copy link
Collaborator

Choose a reason for hiding this comment

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

yes. i meant this is the only acceptable case for now.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Seems like I have to remove the svs_bulk_vectors_add_delete_test unit test as well.
But revert it back in tiered index.
Agree?

Copy link
Collaborator

Choose a reason for hiding this comment

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

yes. In general, consider separting tiered svs and vanila tests

@rfsaliev rfsaliev force-pushed the scalable-vector-search branch from eca8bc4 to 8ed29d2 Compare April 1, 2025 17:00
VecSimIndex_Free(index);
}

TYPED_TEST(SVSTest, resolve_ws_search_runtime_params) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@alonre24 , please check if tests here and below meet this request: #598 (comment)

rfsaliev and others added 4 commits April 2, 2025 15:47
* Add new index algorithm 'SVS' including
  * New SVSindex class
  * SVS index factory
  * SVS-specific index parameters
  * SVS-specific runtime parameters
* Implement SVS unit_test
* Add 'SVS' algorithm support to Python bindings

Co-authored-by: Dorin-Marian Ionita <dorin-marian.ionita@intel.com>
Co-authored-by: Maria Markova <maria.markova@intel.com>
* Resolve formatting issues
* Fix test_svs.py
* Fix CMake options
* Shared ownership of SVS index implementation is removed from SVS_BatchIterator
* More accurate index capacity calculation
* Renamed template parameter for SVSGraphBuilder
* Cleaned up includes
* Refactored SVS index parameters handling
* Added bulk-of-vectors preprocessing feature
* Cleaned artefact comments
@rfsaliev rfsaliev force-pushed the scalable-vector-search branch from b811141 to 1599163 Compare April 2, 2025 13:49
// clang-format off
using SVSDataTypeSet = ::testing::Types<SVSIndexType<VecSimType_FLOAT32, float, VecSimSvsQuant_NONE>
#if HAVE_SVS_LVQ
,SVSIndexType<VecSimType_FLOAT32, float, VecSimSvsQuant_8>
Copy link
Collaborator

Choose a reason for hiding this comment

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

No need to test other quantisation variants?

@rfsaliev rfsaliev force-pushed the scalable-vector-search branch from 2f716e4 to 4d888fa Compare April 3, 2025 14:47
@rfsaliev rfsaliev force-pushed the scalable-vector-search branch 2 times, most recently from 5cd2c63 to 48c5f7f Compare April 3, 2025 15:03
@alonre24 alonre24 merged commit 46bca86 into RedisAI:main Apr 3, 2025
11 of 15 checks passed
Copy link
Collaborator

@alonre24 alonre24 left a comment

Choose a reason for hiding this comment

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

2 issues to deal in next PR

.dataSize = dataSize,
.metric = svsParams.metric,
.blockSize = svsParams.blockSize,
.multi = false,
Copy link
Collaborator

Choose a reason for hiding this comment

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

We need to fail if we are given multi is true until we support this capability - index creation should return null with proper logging.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Assuming there is no way to request creation of SVS index with multi=true because no such option in the SVSParams - in opposite to HNSD or BruteForce where .multi field exists.
Am I wrong?

Copy link
Collaborator

Choose a reason for hiding this comment

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

You are right.
When will we have support to create SVS index of type multi as well?

@@ -1,2 +1,3 @@
pip>=21.1
poetry==1.4.2
mkl-devel>=2025.1.0
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we add the installation of mkl to the install_script.sh using sudo apt install intel-oneapi-mkl-devel instead of here?
Also if you can add this line to the codeql-analysis.yml after "Install Cmake" step that would be great (not a must)

@meiravgri meiravgri mentioned this pull request May 9, 2025
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.

6 participants