Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bindings/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ if (SVS_RUNTIME_ENABLE_LVQ_LEANVEC)
else()
# Links to LTO-enabled static library, requires GCC/G++ 11.2
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "11.2" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "11.3")
set(SVS_URL "https://github.com/intel/ScalableVectorSearch/releases/download/v0.3.0/svs-shared-library-0.3.0-lto-ivf.tar.gz"
set(SVS_URL "https://github.com/intel/ScalableVectorSearch/releases/download/nightly/svs-shared-library-lto-nightly-2026-04-28-1226.tar.gz"
CACHE STRING "URL to download SVS shared library")
else()
message(WARNING
Expand Down
10 changes: 10 additions & 0 deletions bindings/cpp/include/svs/runtime/flat_index.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ struct SVS_RUNTIME_API FlatIndex {

virtual Status save(std::ostream& out) const noexcept = 0;
static Status load(FlatIndex** index, std::istream& in, MetricType metric) noexcept;

// Load from a memory-mapped file.
// The file is expected to be in the format produced by save().
static Status
map_to_file(FlatIndex** index, const char* path, MetricType metric) noexcept;

// Load from a memory buffer.
// The buffer is expected to be in the format produced by save().
static Status
map_to_memory(FlatIndex** index, void* data, size_t size, MetricType metric) noexcept;
};

} // namespace v0
Expand Down
16 changes: 16 additions & 0 deletions bindings/cpp/include/svs/runtime/vamana_index.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,22 @@ struct SVS_RUNTIME_API VamanaIndex {
static Status load(
VamanaIndex** index, std::istream& in, MetricType metric, StorageKind storage_kind
) noexcept;

// Load from a memory-mapped file.
// The file is expected to be in the format produced by save().
static Status map_to_file(
VamanaIndex** index, const char* path, MetricType metric, StorageKind storage_kind
) noexcept;

// Load from a memory buffer.
// The buffer is expected to be in the format produced by save().
static Status map_to_memory(
VamanaIndex** index,
void* data,
size_t size,
MetricType metric,
StorageKind storage_kind
) noexcept;
Comment on lines +98 to +113
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@alexanderguzhva, is this API suitable?

};

struct SVS_RUNTIME_API VamanaIndexLeanVec : public VamanaIndex {
Expand Down
28 changes: 28 additions & 0 deletions bindings/cpp/src/flat_index.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,33 @@ Status FlatIndex::load(FlatIndex** index, std::istream& in, MetricType metric) n
return Status_Ok;
});
}

Status
FlatIndex::map_to_file(FlatIndex** index, const char* path, MetricType metric) noexcept {
*index = nullptr;
return runtime_error_wrapper([&] {
std::filesystem::path fs_path(path);
auto is = std::make_unique<svs::io::mmstream>(fs_path);
std::unique_ptr<FlatIndexImpl> impl{
FlatIndexImpl::map_to_stream(std::move(is), metric)};
*index = new FlatIndexManager{std::move(impl)};
return Status_Ok;
});
}

Status FlatIndex::map_to_memory(
FlatIndex** index, void* data, size_t size, MetricType metric
) noexcept {
*index = nullptr;
return runtime_error_wrapper([&] {
auto sp = std::span(reinterpret_cast<char*>(data), size);
auto is = std::make_unique<svs::io::ispanstream>(sp);
std::unique_ptr<FlatIndexImpl> impl{
FlatIndexImpl::map_to_stream(std::move(is), metric)};
*index = new FlatIndexManager{std::move(impl)};
return Status_Ok;
});
}

} // namespace runtime
} // namespace svs
34 changes: 32 additions & 2 deletions bindings/cpp/src/flat_index_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include <svs/core/data.h>
#include <svs/core/distance.h>
#include <svs/core/io/memstream.h>
#include <svs/core/query_result.h>
#include <svs/orchestrators/exhaustive.h>

Expand Down Expand Up @@ -111,12 +112,39 @@ class FlatIndexImpl {
});
}

static FlatIndexImpl*
map_to_stream(std::unique_ptr<std::istream>&& in, MetricType metric) {
if (!svs::io::is_memory_stream(*in)) {
throw StatusException{
ErrorCode::INVALID_ARGUMENT, "Provided stream is not a memory stream"};
}
auto threadpool = default_threadpool();
using storage_type = svs::runtime::storage::
StorageType_t<StorageKind::FP32, svs::io::MemoryStreamAllocator<float>>;

svs::DistanceDispatcher distance_dispatcher(to_svs_distance(metric));
return distance_dispatcher([&](auto&& distance) {
auto impl = new svs::Flat{svs::Flat::assemble<float, storage_type>(
*in, std::forward<decltype(distance)>(distance), std::move(threadpool)
)};

return new FlatIndexImpl(
std::unique_ptr<svs::Flat>{impl}, metric, std::move(in)
);
});
}

protected:
// Constructor used during loading
FlatIndexImpl(std::unique_ptr<svs::Flat>&& impl, MetricType metric)
FlatIndexImpl(
std::unique_ptr<svs::Flat>&& impl,
MetricType metric,
std::unique_ptr<std::istream> mapped_stream = nullptr
)
: dim_{impl->dimensions()}
, metric_type_{metric}
, impl_{std::move(impl)} {}
, impl_{std::move(impl)}
, mapped_stream_{std::move(mapped_stream)} {}

void init_impl(data::ConstSimpleDataView<float> data) {
auto threadpool = default_threadpool();
Expand All @@ -139,6 +167,8 @@ class FlatIndexImpl {
size_t dim_;
MetricType metric_type_;
std::unique_ptr<svs::Flat> impl_;
// For memory-mapping, we need to keep the stream alive as long as the index is alive
std::unique_ptr<std::istream> mapped_stream_;
};
} // namespace runtime
} // namespace svs
32 changes: 32 additions & 0 deletions bindings/cpp/src/vamana_index.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,38 @@ Status VamanaIndex::load(
});
}

Status VamanaIndex::map_to_file(
VamanaIndex** index, const char* path, MetricType metric, StorageKind storage_kind
) noexcept {
using Impl = VamanaIndexImpl;
*index = nullptr;
return runtime_error_wrapper([&] {
std::filesystem::path fs_path(path);
auto is = std::make_unique<svs::io::mmstream>(fs_path);
std::unique_ptr<Impl> impl{
Impl::map_to_stream(std::move(is), metric, storage_kind)};
*index = new VamanaIndexManagerBase<Impl>{std::move(impl)};
});
}

Status VamanaIndex::map_to_memory(
VamanaIndex** index,
void* data,
size_t size,
MetricType metric,
StorageKind storage_kind
) noexcept {
using Impl = VamanaIndexImpl;
*index = nullptr;
return runtime_error_wrapper([&] {
auto sp = std::span(reinterpret_cast<char*>(data), size);
auto is = std::make_unique<svs::io::ispanstream>(sp);
std::unique_ptr<Impl> impl{
Impl::map_to_stream(std::move(is), metric, storage_kind)};
*index = new VamanaIndexManagerBase<Impl>{std::move(impl)};
});
}
Comment thread
rfsaliev marked this conversation as resolved.

#ifdef SVS_RUNTIME_HAVE_LVQ_LEANVEC
// Specialization to build LeanVec-based Vamana index with specified leanvec dims
Status VamanaIndexLeanVec::build(
Expand Down
44 changes: 39 additions & 5 deletions bindings/cpp/src/vamana_index_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <svs/core/data.h>
#include <svs/core/distance.h>
#include <svs/core/graph.h>
#include <svs/core/io/memstream.h>
#include <svs/core/query_result.h>
#include <svs/extensions/vamana/scalar.h>
#include <svs/lib/file.h>
Expand Down Expand Up @@ -387,14 +388,18 @@ class VamanaIndexImpl {

// Constructor used during loading
VamanaIndexImpl(
std::unique_ptr<svs::Vamana>&& impl, MetricType metric, StorageKind storage_kind
std::unique_ptr<svs::Vamana>&& impl,
MetricType metric,
StorageKind storage_kind,
std::unique_ptr<std::istream> mapped_stream = nullptr
)
: dim_{0}
, metric_type_{metric}
, storage_kind_{storage_kind}
, build_params_{}
, default_search_params_{}
, impl_{std::move(impl)} {
, impl_{std::move(impl)}
, mapped_stream_{std::move(mapped_stream)} {
if (impl_) {
dim_ = impl_->dimensions();
const auto& buffer_config = impl_->get_search_parameters().buffer_config_;
Expand All @@ -410,11 +415,12 @@ class VamanaIndexImpl {
}
}

template <StorageKind Kind, typename Alloc>
template <StorageKind Kind, typename Alloc, typename... Args>
static svs::Vamana* load_impl_t(
storage::StorageType<Kind, Alloc>&& SVS_UNUSED(tag),
std::istream& stream,
MetricType metric
MetricType metric,
Args&&... args
) {
if constexpr (!storage::is_supported_storage_kind_v<Kind>) {
throw StatusException(
Expand All @@ -425,7 +431,10 @@ class VamanaIndexImpl {
auto threadpool = default_threadpool();

return new svs::Vamana(svs::Vamana::assemble<float, storage_type>(
stream, to_svs_distance(metric), std::move(threadpool)
stream,
to_svs_distance(metric),
std::move(threadpool),
std::forward<Args>(args)...
));
}
}
Expand All @@ -447,6 +456,30 @@ class VamanaIndexImpl {
);
}

static VamanaIndexImpl* map_to_stream(
std::unique_ptr<std::istream>&& in, MetricType metric, StorageKind storage_kind
) {
using map_allocator_type = svs::io::MemoryStreamAllocator<float>;
if (!svs::io::is_memory_stream(*in)) {
throw StatusException{
ErrorCode::INVALID_ARGUMENT, "Provided stream is not a memory stream"};
}
return storage::dispatch_storage_kind<map_allocator_type>(
storage_kind,
[&](auto&& tag, std::unique_ptr<std::istream>&& in, MetricType metric) {
using Tag = std::decay_t<decltype(tag)>;
auto impl = load_impl_t(
std::forward<Tag>(tag), *in, metric, map_allocator_type{*in}
);
return new VamanaIndexImpl(
std::unique_ptr<svs::Vamana>{impl}, metric, storage_kind, std::move(in)
);
},
std::move(in),
metric
);
}

// Data members
protected:
size_t dim_;
Expand All @@ -455,6 +488,7 @@ class VamanaIndexImpl {
VamanaIndex::BuildParams build_params_;
VamanaIndex::SearchParams default_search_params_;
std::unique_ptr<svs::Vamana> impl_;
std::unique_ptr<std::istream> mapped_stream_;
};

#ifdef SVS_RUNTIME_HAVE_LVQ_LEANVEC
Expand Down
Loading
Loading