diff --git a/bindings/cpp/include/svs/runtime/api_defs.h b/bindings/cpp/include/svs/runtime/api_defs.h index f77df9d78..9425a8cc4 100644 --- a/bindings/cpp/include/svs/runtime/api_defs.h +++ b/bindings/cpp/include/svs/runtime/api_defs.h @@ -32,7 +32,7 @@ namespace svs { namespace runtime { -namespace v0 { +inline namespace v0 { class OptionalBool { enum class Value : int8_t { Undef = -1, True = 1, False = 0 }; diff --git a/bindings/cpp/include/svs/runtime/dynamic_ivf_index.h b/bindings/cpp/include/svs/runtime/dynamic_ivf_index.h index 39eda2af8..1329b755c 100644 --- a/bindings/cpp/include/svs/runtime/dynamic_ivf_index.h +++ b/bindings/cpp/include/svs/runtime/dynamic_ivf_index.h @@ -24,7 +24,7 @@ namespace svs { namespace runtime { -namespace v0 { +inline namespace v0 { /// @brief Abstract interface for dynamic IVF indices (supports add/delete). struct SVS_RUNTIME_API DynamicIVFIndex : public IVFIndex { @@ -69,8 +69,8 @@ struct SVS_RUNTIME_API DynamicIVFIndex : public IVFIndex { /// @param x Pointer to vector data (row-major, n x dimensions). /// @param reuse_empty Whether to reuse empty slots from deleted vectors. /// @return Status indicating success or error. - virtual Status - add(size_t n, const size_t* labels, const float* x, bool reuse_empty = false + virtual Status add( + size_t n, const size_t* labels, const float* x, bool reuse_empty = false ) noexcept = 0; /// @brief Remove vectors from the index by ID. diff --git a/bindings/cpp/include/svs/runtime/dynamic_vamana_index.h b/bindings/cpp/include/svs/runtime/dynamic_vamana_index.h index 1d487baf9..e716c81f8 100644 --- a/bindings/cpp/include/svs/runtime/dynamic_vamana_index.h +++ b/bindings/cpp/include/svs/runtime/dynamic_vamana_index.h @@ -25,7 +25,7 @@ namespace svs { namespace runtime { -namespace v0 { +inline namespace v0 { // Abstract interface for Dynamic Vamana-based indexes. struct SVS_RUNTIME_API DynamicVamanaIndex : public VamanaIndex { @@ -39,8 +39,8 @@ struct SVS_RUNTIME_API DynamicVamanaIndex : public VamanaIndex { // Utility function to check storage kind support static Status check_storage_kind(StorageKind storage_kind) noexcept; - static Status check_params(const VamanaIndex::DynamicIndexParams& dynamic_index_params - ) noexcept; + static Status + check_params(const VamanaIndex::DynamicIndexParams& dynamic_index_params) noexcept; // Static constructors and destructors // ABI backward compatibility diff --git a/bindings/cpp/include/svs/runtime/flat_index.h b/bindings/cpp/include/svs/runtime/flat_index.h index 9c635e35d..752dc3b2e 100644 --- a/bindings/cpp/include/svs/runtime/flat_index.h +++ b/bindings/cpp/include/svs/runtime/flat_index.h @@ -23,7 +23,7 @@ namespace svs { namespace runtime { -namespace v0 { +inline namespace v0 { // Abstract interface for Flat indices. struct SVS_RUNTIME_API FlatIndex { diff --git a/bindings/cpp/include/svs/runtime/ivf_index.h b/bindings/cpp/include/svs/runtime/ivf_index.h index 468117436..1f398ec36 100644 --- a/bindings/cpp/include/svs/runtime/ivf_index.h +++ b/bindings/cpp/include/svs/runtime/ivf_index.h @@ -24,7 +24,7 @@ namespace svs { namespace runtime { -namespace v0 { +inline namespace v0 { // Abstract interface for IVF (Inverted File) indices. struct SVS_RUNTIME_API IVFIndex { diff --git a/bindings/cpp/include/svs/runtime/training.h b/bindings/cpp/include/svs/runtime/training.h index da072e42b..22d731a38 100644 --- a/bindings/cpp/include/svs/runtime/training.h +++ b/bindings/cpp/include/svs/runtime/training.h @@ -23,7 +23,7 @@ namespace svs { namespace runtime { -namespace v0 { +inline namespace v0 { struct SVS_RUNTIME_API LeanVecTrainingData { virtual ~LeanVecTrainingData(); diff --git a/bindings/cpp/include/svs/runtime/vamana_index.h b/bindings/cpp/include/svs/runtime/vamana_index.h index 2149c0d3e..ece34b29d 100644 --- a/bindings/cpp/include/svs/runtime/vamana_index.h +++ b/bindings/cpp/include/svs/runtime/vamana_index.h @@ -23,7 +23,7 @@ namespace svs { namespace runtime { -namespace v0 { +inline namespace v0 { namespace detail { struct VamanaBuildParameters { diff --git a/bindings/cpp/include/svs/runtime/version.h b/bindings/cpp/include/svs/runtime/version.h index bdf56450d..eaa6fac95 100644 --- a/bindings/cpp/include/svs/runtime/version.h +++ b/bindings/cpp/include/svs/runtime/version.h @@ -89,7 +89,7 @@ inline namespace v0 { /// /// @brief Version information structure for runtime queries /// -namespace svs::runtime::v0 { +namespace svs::runtime::inline v0 { struct VersionInfo { static constexpr int major = SVS_RUNTIME_VERSION_MAJOR; @@ -110,4 +110,4 @@ struct VersionInfo { } }; -} // namespace svs::runtime::v0 +} // namespace svs::runtime::inline v0 diff --git a/include/svs/index/ivf/index.h b/include/svs/index/ivf/index.h index 00f45735a..c4e39db6e 100644 --- a/include/svs/index/ivf/index.h +++ b/include/svs/index/ivf/index.h @@ -151,6 +151,12 @@ class IVFIndex { , inter_query_threadpool_{threads::as_threadpool(std::move(threadpool_proto))} , intra_query_thread_count_{intra_query_thread_count} , logger_{std::move(logger)} { + // Clamp thread pool: more threads than centroids causes OOB in + // compute_centroid_distances and wastes resources. + if (inter_query_threadpool_.size() > centroids_.size()) { + inter_query_threadpool_ = + InterQueryThreadPool(threads::DefaultThreadPool(centroids_.size())); + } validate_thread_configuration(); initialize_thread_pools(); initialize_search_buffers(); @@ -269,7 +275,8 @@ class IVFIndex { return scratchspace_type{ create_centroid_buffer(sp.n_probes_), create_leaf_buffers(buffer_leaves_size), - extensions::per_thread_batch_search_setup(cluster0_, distance_)}; + extensions::per_thread_batch_search_setup(cluster0_, distance_) + }; } /// @brief Return scratch space resources for external threading with default parameters @@ -540,7 +547,8 @@ class IVFIndex { mutable std::vector id_in_cluster_{}; // Thread-safe initialization flag for ID mapping (wrapped in unique_ptr for movability) mutable std::unique_ptr id_mapping_init_flag_{ - std::make_unique()}; + std::make_unique() + }; ///// Threading Infrastructure ///// InterQueryThreadPool inter_query_threadpool_; // Handles parallelism across queries @@ -567,9 +575,11 @@ class IVFIndex { void initialize_thread_pools() { // Create thread pools for intra-query (cluster-level) parallelism for (size_t i = 0; i < inter_query_threadpool_.size(); i++) { - intra_query_threadpools_.push_back(threads::ThreadPoolHandle( - threads::DefaultThreadPool(intra_query_thread_count_) - )); + intra_query_threadpools_.push_back( + threads::ThreadPoolHandle( + threads::DefaultThreadPool(intra_query_thread_count_) + ) + ); } } @@ -627,11 +637,13 @@ class IVFIndex { void validate_query_batch_size(size_t query_size) const { if (query_size > MAX_QUERY_BATCH_SIZE) { - throw std::runtime_error(fmt::format( - "Query batch size {} exceeds maximum allowed {}", - query_size, - MAX_QUERY_BATCH_SIZE - )); + throw std::runtime_error( + fmt::format( + "Query batch size {} exceeds maximum allowed {}", + query_size, + MAX_QUERY_BATCH_SIZE + ) + ); } } @@ -645,9 +657,11 @@ class IVFIndex { std::vector>> buffers; buffers.reserve(intra_query_thread_count_); for (size_t j = 0; j < intra_query_thread_count_; j++) { - buffers.push_back(SortedBuffer>( - buffer_size, distance::comparator(distance_) - )); + buffers.push_back( + SortedBuffer>( + buffer_size, distance::comparator(distance_) + ) + ); } return buffers; } diff --git a/include/svs/index/ivf/sorted_buffer.h b/include/svs/index/ivf/sorted_buffer.h index 85939ef6d..d028063be 100644 --- a/include/svs/index/ivf/sorted_buffer.h +++ b/include/svs/index/ivf/sorted_buffer.h @@ -139,7 +139,7 @@ template > class SortedBuffer { /// @brief Return ``true`` if a neighbor with the given distance can be skipped. /// bool can_skip(float distance) const { - return compare_(back().distance(), distance) && full(); + return full() && compare_(back().distance(), distance); } /// diff --git a/include/svs/lib/float16.h b/include/svs/lib/float16.h index 108c46eac..5276be4ca 100644 --- a/include/svs/lib/float16.h +++ b/include/svs/lib/float16.h @@ -56,10 +56,14 @@ inline uint16_t float_to_float16_untyped_slow(const float x) { const uint32_t b = bitcast_float_to_uint32(x) + 0x00001000; const uint32_t e = (b & 0x7F800000) >> 23; // exponent const uint32_t m = b & 0x007FFFFF; // mantissa + // Compute denormalized shift safely: only valid when 101 < e < 113, + // which means shift amount (125 - e) is in range [13, 23]. + // Clamp e to avoid undefined behavior when e > 125 or e < 102. + const uint32_t safe_e = (e > 101 && e < 113) ? e : 112; return (b & 0x80000000) >> 16 | static_cast(e > 112) * ((((e - 112) << 10) & 0x7C00) | m >> 13) | static_cast((e < 113) && (e > 101)) * - ((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) | + ((((0x007FF000 + m) >> (125 - safe_e)) + 1) >> 1) | static_cast(e > 143) * 0x7FFF; // sign : normalized : denormalized : saturate } diff --git a/include/svs/lib/threads/thread.h b/include/svs/lib/threads/thread.h index 5a5776ce5..80be8c846 100644 --- a/include/svs/lib/threads/thread.h +++ b/include/svs/lib/threads/thread.h @@ -855,8 +855,10 @@ template class ThreadImpl { // Wait for the future to become available and rethrow the exception. wait_for_result(); get_result(); - throw ANNEXCEPTION("Expected to get an exception from a crashed thread but no " - "exception was thrown!"); + throw ANNEXCEPTION( + "Expected to get an exception from a crashed thread but no " + "exception was thrown!" + ); } // Assign Work @@ -888,7 +890,7 @@ template class ThreadImpl { // * Catch this exception and wrap its message inside a `ThreadError`. try { unsafe_assign(fn); - } catch (const ThreadCrashedError& err) { + } catch (const ThreadCrashedError&) { try { unsafe_get_exception(); } catch (const std::exception& inner_error) { throw ThreadError{inner_error}; }