Skip to content

fix(orchestrators): route stream assemble through the loader-based assemble - #417

Merged
ethanglaser merged 13 commits into
mainfrom
fix/stream-assemble-data-loader-args
Oct 16, 2026
Merged

ethanglaser merged 13 commits into
mainfrom
fix/stream-assemble-data-loader-args

Conversation

@ahuber21

@ahuber21 ahuber21 commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Loading a Vamana-family index from a stream now goes through the same assemble(config_proto, graph_loader, data_loader, ...) entry point as loading from disk, so a stream is just one more kind of config source rather than its own code path.

Stream save/load was added after the loader-based interface already existed, and it took a shortcut. Instead of handing assemble a config, a graph loader and a data loader, the stream overload took the stream plus a pack of arguments for the data, and decided internally how the graph was read. The graph allocator was fixed, and debug_load_from_static was not exposed. That was fine while one default allocator was enough. It stopped being fine once the C API needed to put the graph and the data in different allocators. #402 and the first version of this PR tried to close that gap by adding parameters and overloads to the stream path, and every attempt made the stream path more of a parallel copy of the loader path. This PR stops patching the copy and removes it.

This PR goes back to the original design. The loader-based assemble on Vamana, DynamicVamana and ConcurrentDynamicVamana now accepts a std::istream as config_proto. In that case it runs the existing stream logic unchanged: it detects native vs. directory-archive format, reads the native stream in place, or unpacks the archive and loads from paths. The graph and data allocators come from the loaders, exactly as they do for file loads. The released assemble<Q, Data>(stream, distance, pool, data_args...) stays as a thin wrapper with its v0.4.0 signature and default allocators, so existing callers compile and behave as before. The C API and the C++ runtime now call the loader-based form directly, and neither public binding header changes.

svs::StreamLoader<T, Args...> lives in svs/core/loading.h, next to dispatch_load. It plays the role on the stream side that GraphLoader and VectorDataLoader play for files: it fixes the type to load and carries the arguments for it, typically an allocator. Unlike those two it holds no source. The orchestrator hands it either the stream or the unpacked component directory, because only the orchestrator knows which format arrived. One generic template is enough, since the caller always knows the exact type and graphs, datasets and configs all already load from both a stream and a path. A file-side loader like VectorDataLoader also deduces element type and dimensions, which a stream load never needs. static_asserts reject mixing a StreamLoader with a non-stream config, in either direction. A new public alias, Vamana::default_graph_type<Data>, names the default graph type. For view-backed data it rebinds the data's allocator to uint32_t; otherwise it uses HugepageAllocator. The wrapper and the C++ runtime share that one rule instead of each keeping a copy.

A few things a reviewer should know:

  • The stream path now honours logger and debug_load_from_static. That is a behaviour change, though only for callers who were passing values that were being ignored.
  • StreamLoader stores copies of its arguments and passes them on as const lvalues. A loader that needs to consume an rvalue would fail to compile; it would not misbehave silently.
  • Flat, DynamicFlat, IVF and DynamicIVF keep their dedicated stream overloads. Flat's loader-based assemble has no config slot, and IVF has no loader-based assemble at all, so bringing them in line is separate work.
  • The index-level stream auto_assemble functions in svs::index::* are untouched.

@ahuber21
ahuber21 force-pushed the fix/stream-assemble-data-loader-args branch from a2f6a28 to 416b0f9 Compare October 9, 2026 14:20
@ahuber21
ahuber21 marked this pull request as ready for review October 9, 2026 14:20
@ahuber21 ahuber21 changed the title fix(orchestrators): forward data loader arguments in stream assemble fix(orchestrators): StreamGraphLoader to forward template args in stream assemble Oct 12, 2026
Comment thread include/svs/core/graph.h Outdated
/// allocator type.
///
template <typename Idx = uint32_t, typename Allocator = HugepageAllocator<Idx>>
struct StreamGraphLoader {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

IMO, there is unnecessary code duplication.
It would be better to reuse existing GraphLoader class constructed from (stream, allocator) rather than multiply entities.

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.

Thanks. Good idea. I will try it this way.

ahuber21 and others added 2 commits October 13, 2026 02:37
StreamLoader carries the element type and loader arguments (e.g. an
allocator) for one component of an index saved to a stream. The
orchestrator hands it either the stream (native format) or the
unpacked component directory (directory archive).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…semble

Vamana, DynamicVamana and ConcurrentDynamicVamana now load from a stream
through the existing assemble(config_proto, graph_loader, data_loader,
...) when config_proto is a std::istream and both loaders are
StreamLoader. The stream body moved unchanged into a private helper.

- The released assemble<Q, Data>(stream, distance, pool, data_args...)
  on Vamana and DynamicVamana stays as a thin wrapper with its v0.4.0
  signature and default graph allocators.
- Removed, never released: Vamana's view-backed overload and the
  (data_allocator, graph_allocator) overloads from #402, and
  ConcurrentDynamicVamana's stream overload from #413.
- The stream path used to drop the logger and debug_load_from_static;
  both are now passed through.
- The C API and C++ runtime call the loader-based form. Public binding
  headers are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ahuber21
ahuber21 force-pushed the fix/stream-assemble-data-loader-args branch from bc42712 to 4f2975f Compare October 13, 2026 09:59
@ahuber21 ahuber21 changed the title fix(orchestrators): StreamGraphLoader to forward template args in stream assemble fix(orchestrators): route stream assemble through the loader-based assemble Oct 13, 2026
@ahuber21

Copy link
Copy Markdown
Member Author

@rfsaliev yet another attempts to harmonize assemble() calls - PR description updated.
As usual, static lib CI tests require uploading new artifacts. I'll fix that before merging, and the changes are ready for comments nonetheless.

@ahuber21
ahuber21 requested a review from rfsaliev October 13, 2026 10:29
svs::ConcurrentDynamicVamana::assemble<float, ConcurrentData>(
stream, svs::distance::DistanceL2(), num_threads
);
svs::DynamicVamana loaded = svs::ConcurrentDynamicVamana::assemble<float>(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Consider adding the same tests for other affected orchestrators.

Comment thread include/svs/orchestrators/vamana.h Outdated
…loads

Vamana, DynamicVamana and ConcurrentDynamicVamana now have a dedicated
assemble(std::istream&, StreamLoader graph, StreamLoader data, ...)
overload instead of branching inside the file-layout assemble. The
file-layout overload is constrained to exclude streams and StreamLoader,
so a mismatched call finds no viable overload rather than hitting a
static_assert. The stream body moved unchanged; call sites are
unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rfsaliev

Copy link
Copy Markdown
Member

/intelci

ahuber21 and others added 2 commits October 14, 2026 00:37
The static-library C API job builds against the headers shipped in the
prebuilt archive. The previous archive predates `svs::StreamLoader` and
the StreamLoader assemble overloads, so the dispatchers failed to
compile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The static-library C++ runtime job builds against the headers shipped
in the prebuilt archive. The previous archive predates `svs::StreamLoader`
and `Vamana::default_graph_type`, so the runtime impls failed to compile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ahuber21

Copy link
Copy Markdown
Member Author

/intelci

@rfsaliev rfsaliev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There are some suggestions.
And seems like the complex refactoring is needed for save/load index cases.
But functionally this PR looks good for me.
Approving.

Comment thread include/svs/core/loading.h

T load(std::istream& stream) const {
return std::apply(
[&](const Args&... args) { return lib::load_from_stream<T>(stream, args...); },

@rfsaliev rfsaliev Oct 14, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

what if one of Args is not copyable, i.e. has deleted copy constructor?

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.

It works so long it's passed as an rvalue. Will add a test case showcasing it. StreamLoader can of course always be extended if more scenarios are needed. For now, everything compiles though.

Comment thread include/svs/orchestrators/concurrent_dynamic_vamana.h Outdated
ahuber21 and others added 5 commits October 15, 2026 00:17
The static-library C API job builds against the headers shipped in the
prebuilt archive. The previous archive predates `svs::make_stream_loader`,
which the stream dispatchers now call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The static-library C++ runtime job builds against the headers shipped
in the prebuilt archive. The previous archive predates
`svs::make_stream_loader`, which the runtime impls now call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@rfsaliev rfsaliev left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGFM, just nitpick

Comment thread include/svs/orchestrators/dynamic_vamana.h Outdated

@ethanglaser ethanglaser left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, additional test coverage etc can be added as follow-up but this fixes CI and should be part of release

@ethanglaser
ethanglaser merged commit cde3771 into main Oct 16, 2026
22 checks passed
@ethanglaser
ethanglaser deleted the fix/stream-assemble-data-loader-args branch October 16, 2026 21:06
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.

4 participants