Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions .github/workflows/build-macos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ jobs:
strategy:
matrix:
build_type: [RelWithDebugInfo]
cxx: [clang++-15]
cxx: [clang++-19]
include:
- cxx: clang++-15
package: llvm@15
- cxx: clang++-19
package: llvm@19
cc_name: clang
cxx_name: clang++
needs_prefix: true
Expand Down
7 changes: 5 additions & 2 deletions bindings/python/include/svs/python/manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ pybind11::tuple py_search(
matrix_view(result_idx), matrix_view(result_dists)
);

svs::index::search_batch_into(self, q_result, query_data.cview());
{
pybind11::gil_scoped_release release;
svs::index::search_batch_into(self, q_result, query_data.cview());
}
return pybind11::make_tuple(result_idx, result_dists);
}

Expand Down Expand Up @@ -86,7 +89,7 @@ void add_threading_interface(pybind11::class_<Manager>& manager) {
"num_threads",
&Manager::get_num_threads,
[](Manager& self, int num_threads) {
self.set_threadpool(svs::threads::DefaultThreadPool(num_threads));
self.set_threadpool(svs::threads::SwitchNativeThreadPool(num_threads));
},
"Read/Write (int): Get and set the number of threads used to process queries."
);
Expand Down
27 changes: 23 additions & 4 deletions bindings/python/src/dynamic_vamana.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,12 @@ void add_points(
"Expected IDs to be the same length as the number of rows in points!"
);
}
index.add_points(data_view(py_data), std::span(ids.data(), ids.size()), reuse_empty);
auto data = data_view(py_data);
auto id_span = std::span(ids.data(), ids.size());
{
py::gil_scoped_release release;
index.add_points(data, id_span, reuse_empty);
}
}

const char* ADD_POINTS_DOCSTRING = R"(
Expand Down Expand Up @@ -381,8 +386,18 @@ void wrap(py::module& m) {

add_dynamic_vamana_properties(vamana);

vamana.def("consolidate", &svs::DynamicVamana::consolidate, CONSOLIDATE_DOCSTRING);
vamana.def("compact", &svs::DynamicVamana::compact, COMPACT_DOCSTRING);
vamana.def(
"consolidate",
&svs::DynamicVamana::consolidate,
py::call_guard<py::gil_scoped_release>(),
CONSOLIDATE_DOCSTRING
);
vamana.def(
"compact",
&svs::DynamicVamana::compact,
py::call_guard<py::gil_scoped_release>(),
COMPACT_DOCSTRING
);

// Reloading
vamana.def(
Expand Down Expand Up @@ -435,7 +450,11 @@ void wrap(py::module& m) {
vamana.def(
"delete",
[](svs::DynamicVamana& index, const py_contiguous_array_t<size_t>& ids) {
index.delete_points(as_span(ids));
auto id_span = as_span(ids);
{
py::gil_scoped_release release;
index.delete_points(id_span);
}
},
py::arg("ids"),
DELETE_DOCSTRING
Expand Down
2 changes: 1 addition & 1 deletion bindings/python/src/python_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ class ScopedModuleNameOverride {

} // namespace

PYBIND11_MODULE(_svs, m) {
PYBIND11_MODULE(_svs, m, py::mod_gil_not_used()) {
// Internally, the top level `__init__.py` imports everything from the C++ module named
// `_svs`.
//
Expand Down
22 changes: 16 additions & 6 deletions include/svs/concepts/graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@

namespace svs::graphs {

/// Outcome of `MemoryGraph::add_edge(src, dst)`. Distinguishes three cases so callers
/// can route dropped edges (e.g. to a backedge buffer) without a TOCTOU race between a
/// pre-check and the insert.
enum class AddEdgeResult : uint8_t {
Added, // Edge was inserted.
AlreadyExists, // Edge was already present (or self-loop). Not inserted.
Full, // Node's adjacency list is at max_degree. Edge NOT inserted.
};

// clang-format off

///
Expand Down Expand Up @@ -135,12 +144,13 @@ template <ImmutableMemoryGraph G> using index_type_t = typename G::index_type;
/// @code{.cpp}
/// template <typename T>
/// concept MemoryGraph = requires(T& g, const T& const_g) {
/// // Add an edge to the graph.
/// // Must return the out degree of `src` after adding the edge `src -> dst`.
/// // If adding the edge would result in the graph exceeding its maximum degree,
/// // implementations are free to not add this edge.
/// // Add an edge to the graph atomically. Returns an AddEdgeResult indicating:
/// // Added - edge was inserted
/// // AlreadyExists - edge was already present (or self-loop); no insert
/// // Full - node is at max_degree; edge NOT inserted (caller should
/// // route to an overflow buffer if needed)
/// requires requires(index_type_t<T> src, index_type_t<T> dst) {
/// { g.add_edge(src, dst) } -> std::convertible_to<size_t>;
/// { g.add_edge(src, dst) } -> std::convertible_to<AddEdgeResult>;
/// };
///
/// // Completely clear the adjacency list for vertex ``i``.
Expand All @@ -164,7 +174,7 @@ template <typename T>
concept MemoryGraph = requires(T& g, const T& const_g) {
// Adding an edge.
requires requires(index_type_t<T> src, index_type_t<T> dst) {
{ g.add_edge(src, dst) } -> std::convertible_to<size_t>;
{ g.add_edge(src, dst) } -> std::convertible_to<AddEdgeResult>;
};

// Clear adjacency list.
Expand Down
Loading
Loading