From 0fa88d61b283b852ef73b873fa5eb91c0d075d20 Mon Sep 17 00:00:00 2001 From: ruvnet Date: Sat, 16 May 2026 08:30:31 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20batch=201=20=E2=80=94=20deadlock,=20AVX-?= =?UTF-8?q?512=20gating,=20Windows=20case-collisions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #437: VectorDb::delete in ruvector-router-core acquired the stats RwLock twice in one statement. parking_lot::RwLock is non-reentrant, so the second .write() deadlocked against the first guard's lifetime. Bind the guard once. Closes #438: Gate AVX-512 intrinsics behind a new `simd-avx512` Cargo feature (default-on). Lets downstream consumers on stable Rust 1.77–1.88 (before avx512f stabilization in 1.89) opt out without forcing nightly: cargo build --no-default-features --features simd,storage,hnsw,api-embeddings,parallel Runtime dispatch falls back to AVX2 + FMA when the feature is disabled. All 4 #[target_feature(enable = "avx512f")] sites + 4 dispatch branches updated. Both feature configurations verified to compile cleanly; all 18 simd_intrinsics tests pass. Closes #458: Rename two pairs of case-colliding research artifacts under docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/ that broke `git clone` on Windows/NTFS: tmux.js → tmux_lc.js (TMUX.js kept) type.js → type_lc.js (Type.js kept) modules-manifest.json updated to match. Co-Authored-By: claude-flow --- crates/ruvector-core/Cargo.toml | 7 ++- crates/ruvector-core/src/simd_intrinsics.rs | 48 ++++++++++++------- crates/ruvector-router-core/src/vector_db.rs | 3 +- .../versions/v2.1.x/modules-manifest.json | 4 +- .../{tmux.js => tmux_lc.js} | 0 .../{type.js => type_lc.js} | 0 6 files changed, 42 insertions(+), 20 deletions(-) rename docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/react_memo_cache_sentinel/{tmux.js => tmux_lc.js} (100%) rename docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/react_memo_cache_sentinel/{type.js => type_lc.js} (100%) diff --git a/crates/ruvector-core/Cargo.toml b/crates/ruvector-core/Cargo.toml index 52bb4f33..a20467c3 100644 --- a/crates/ruvector-core/Cargo.toml +++ b/crates/ruvector-core/Cargo.toml @@ -95,8 +95,13 @@ name = "bench_memory" harness = false [features] -default = ["simd", "storage", "hnsw", "api-embeddings", "parallel"] +default = ["simd", "simd-avx512", "storage", "hnsw", "api-embeddings", "parallel"] simd = ["simsimd"] # SIMD acceleration (not available in WASM) +# AVX-512 intrinsics. Requires Rust >= 1.89 (where avx512f stabilized). +# Downstream consumers on older stable (1.77–1.88) can opt out: +# --no-default-features --features simd,storage,hnsw,api-embeddings,parallel +# See issue #438. +simd-avx512 = [] parallel = ["rayon", "crossbeam"] # Parallel processing (not available in WASM) storage = ["redb", "memmap2"] # File-based storage (not available in WASM) hnsw = ["hnsw_rs"] # HNSW indexing (not available in WASM due to mmap dependency) diff --git a/crates/ruvector-core/src/simd_intrinsics.rs b/crates/ruvector-core/src/simd_intrinsics.rs index fcb7f8a7..74c61e10 100644 --- a/crates/ruvector-core/src/simd_intrinsics.rs +++ b/crates/ruvector-core/src/simd_intrinsics.rs @@ -42,9 +42,13 @@ const PREFETCH_DISTANCE: usize = 64; pub fn euclidean_distance_simd(a: &[f32], b: &[f32]) -> f32 { #[cfg(target_arch = "x86_64")] { - if is_x86_feature_detected!("avx512f") { - unsafe { euclidean_distance_avx512_impl(a, b) } - } else if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") { + #[cfg(feature = "simd-avx512")] + { + if is_x86_feature_detected!("avx512f") { + return unsafe { euclidean_distance_avx512_impl(a, b) }; + } + } + if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") { unsafe { euclidean_distance_avx2_fma_impl(a, b) } } else if is_x86_feature_detected!("avx2") { unsafe { euclidean_distance_avx2_impl(a, b) } @@ -192,7 +196,7 @@ unsafe fn euclidean_distance_avx2_fma_impl(a: &[f32], b: &[f32]) -> f32 { // ============================================================================ /// AVX-512 euclidean distance - processes 16 floats per iteration -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", feature = "simd-avx512"))] #[target_feature(enable = "avx512f")] unsafe fn euclidean_distance_avx512_impl(a: &[f32], b: &[f32]) -> f32 { assert_eq!(a.len(), b.len(), "Input arrays must have the same length"); @@ -223,7 +227,7 @@ unsafe fn euclidean_distance_avx512_impl(a: &[f32], b: &[f32]) -> f32 { } /// AVX-512 dot product - processes 16 floats per iteration -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", feature = "simd-avx512"))] #[target_feature(enable = "avx512f")] unsafe fn dot_product_avx512_impl(a: &[f32], b: &[f32]) -> f32 { assert_eq!(a.len(), b.len(), "Input arrays must have the same length"); @@ -249,7 +253,7 @@ unsafe fn dot_product_avx512_impl(a: &[f32], b: &[f32]) -> f32 { } /// AVX-512 cosine similarity - processes 16 floats per iteration -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", feature = "simd-avx512"))] #[target_feature(enable = "avx512f")] unsafe fn cosine_similarity_avx512_impl(a: &[f32], b: &[f32]) -> f32 { assert_eq!(a.len(), b.len(), "Input arrays must have the same length"); @@ -284,7 +288,7 @@ unsafe fn cosine_similarity_avx512_impl(a: &[f32], b: &[f32]) -> f32 { } /// AVX-512 Manhattan distance - processes 16 floats per iteration -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", feature = "simd-avx512"))] #[target_feature(enable = "avx512f")] unsafe fn manhattan_distance_avx512_impl(a: &[f32], b: &[f32]) -> f32 { assert_eq!(a.len(), b.len(), "Input arrays must have the same length"); @@ -783,9 +787,13 @@ unsafe fn manhattan_distance_neon_unrolled_impl(a: &[f32], b: &[f32]) -> f32 { pub fn dot_product_simd(a: &[f32], b: &[f32]) -> f32 { #[cfg(target_arch = "x86_64")] { - if is_x86_feature_detected!("avx512f") { - unsafe { dot_product_avx512_impl(a, b) } - } else if is_x86_feature_detected!("avx2") { + #[cfg(feature = "simd-avx512")] + { + if is_x86_feature_detected!("avx512f") { + return unsafe { dot_product_avx512_impl(a, b) }; + } + } + if is_x86_feature_detected!("avx2") { unsafe { dot_product_avx2_impl(a, b) } } else { dot_product_scalar(a, b) @@ -847,9 +855,13 @@ unsafe fn dot_product_avx2_impl(a: &[f32], b: &[f32]) -> f32 { pub fn cosine_similarity_simd(a: &[f32], b: &[f32]) -> f32 { #[cfg(target_arch = "x86_64")] { - if is_x86_feature_detected!("avx512f") { - unsafe { cosine_similarity_avx512_impl(a, b) } - } else if is_x86_feature_detected!("avx2") { + #[cfg(feature = "simd-avx512")] + { + if is_x86_feature_detected!("avx512f") { + return unsafe { cosine_similarity_avx512_impl(a, b) }; + } + } + if is_x86_feature_detected!("avx2") { unsafe { cosine_similarity_avx2_impl(a, b) } } else { cosine_similarity_scalar(a, b) @@ -883,9 +895,13 @@ pub fn cosine_similarity_avx2(a: &[f32], b: &[f32]) -> f32 { pub fn manhattan_distance_simd(a: &[f32], b: &[f32]) -> f32 { #[cfg(target_arch = "x86_64")] { - if is_x86_feature_detected!("avx512f") { - unsafe { manhattan_distance_avx512_impl(a, b) } - } else if is_x86_feature_detected!("avx2") { + #[cfg(feature = "simd-avx512")] + { + if is_x86_feature_detected!("avx512f") { + return unsafe { manhattan_distance_avx512_impl(a, b) }; + } + } + if is_x86_feature_detected!("avx2") { unsafe { manhattan_distance_avx2_impl(a, b) } } else { manhattan_distance_scalar(a, b) diff --git a/crates/ruvector-router-core/src/vector_db.rs b/crates/ruvector-router-core/src/vector_db.rs index eacb8035..a0b38f09 100644 --- a/crates/ruvector-router-core/src/vector_db.rs +++ b/crates/ruvector-router-core/src/vector_db.rs @@ -150,7 +150,8 @@ impl VectorDB { if deleted { self.index.remove(id)?; - self.stats.write().total_vectors = self.stats.write().total_vectors.saturating_sub(1); + let mut stats = self.stats.write(); + stats.total_vectors = stats.total_vectors.saturating_sub(1); } Ok(deleted) diff --git a/docs/research/claude-code-rvsource/versions/v2.1.x/modules-manifest.json b/docs/research/claude-code-rvsource/versions/v2.1.x/modules-manifest.json index 639e90a1..696b14d6 100644 --- a/docs/research/claude-code-rvsource/versions/v2.1.x/modules-manifest.json +++ b/docs/research/claude-code-rvsource/versions/v2.1.x/modules-manifest.json @@ -1418,7 +1418,7 @@ "size": 387 }, { - "name": "type.js", + "name": "type_lc.js", "size": 387 }, { @@ -2330,7 +2330,7 @@ "size": 176 }, { - "name": "tmux.js", + "name": "tmux_lc.js", "size": 176 }, { diff --git a/docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/react_memo_cache_sentinel/tmux.js b/docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/react_memo_cache_sentinel/tmux_lc.js similarity index 100% rename from docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/react_memo_cache_sentinel/tmux.js rename to docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/react_memo_cache_sentinel/tmux_lc.js diff --git a/docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/react_memo_cache_sentinel/type.js b/docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/react_memo_cache_sentinel/type_lc.js similarity index 100% rename from docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/react_memo_cache_sentinel/type.js rename to docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/react_memo_cache_sentinel/type_lc.js