shyjsarah commented on code in PR #82:
URL: 
https://github.com/apache/paimon-vector-index/pull/82#discussion_r3903222561


##########
core/src/pq.rs:
##########
@@ -436,7 +535,29 @@ impl ProductQuantizer {
                             let c_off = c_base + j * chunk_dim;
                             fvec_norm_l2sqr(&self.centroids[c_off..c_off + 
chunk_dim])
                         };
-                        table[t_base + j] = (q_norm + c_norm - 2.0 * 
table[t_base + j]).max(0.0);
+                        let approximate = q_norm + c_norm - 2.0 * table[t_base 
+ j];
+                        // Cover the three reductions and final subtraction 
before trusting SGEMM.
+                        let error_bound =
+                            16.0 * chunk_dim as f32 * f32::EPSILON * (q_norm + 
c_norm);
+                        table[t_base + j] =
+                            if !approximate.is_finite() || approximate <= 
error_bound {

Review Comment:
   **[Major correctness]** This fallback only asks whether each individual 
expanded distance is near its own cancellation bound. It does not protect the 
ordering when two large, nonzero candidate distances differ by less than their 
numerical error.
   
   I reproduced this on `7296eaa`: the direct/FMA add path selects code 1 
(`88314130 < 88314136`), but ordinary in-memory search, unoptimized reader 
search, ephemeral reuse, resident precomputation, and optimized 
serialized-reader search all rank code 0 first. The new per-sub exact fallback 
is not triggered for this subspace.
   
   Please make the direct-encoded 8-bit table contract canonical across all 
search paths, or use a pairwise error/margin rule that recomputes candidates 
whose ordering is not provably stable. The ephemeral f64-to-f32 path also needs 
to preserve the same f32/FMA ordering used for persisted code assignment.



##########
core/src/pq.rs:
##########
@@ -370,6 +386,87 @@ impl ProductQuantizer {
         );
     }
 
+    /// Blocked batch encode for the IVF-PQ add path.
+    ///
+    /// The nbits=8 path uses a transposed-codebook kernel: per sub-quantizer
+    /// the centroids are transposed once to `[dsub][ksub]` so the inner
+    /// distance loop is stride-1 over `ksub` and runs on SIMD (NEON/AVX2,
+    /// scalar fallback). This removes the per-vector-per-sub GEMM calls and
+    /// their distance-table memory traffic. Distances are accumulated directly
+    /// from coordinate differences, avoiding the cancellation possible in the
+    /// norm/dot identity used by [`Self::encode_batch`]. The different 
arithmetic
+    /// can produce different valid codes, so use this only where codes are 
freshly
+    /// produced (index build), not where byte-stable output is pinned. Trained
+    /// codebooks are expected to contain only finite values.
+    pub(crate) fn encode_batch_blocked(&self, data: &[f32], n: usize, codes: 
&mut [u8]) {
+        // The 4-bit packed path keeps the original per-vector implementation.
+        if self.nbits == 8 && (0..self.m).all(|sub| self.chunk_dim(sub) >= 4) {
+            self.encode_batch_8bit_transposed(data, n, codes);

Review Comment:
   **[Major x86_64 fallback regression]** This now transposes the full codebook 
for every eligible add, even when runtime dispatch selects the scalar scorer. 
For `d=768,m=192`, that is about 0.75 MiB of allocation/writes before 
processing a single row.
   
   On a supported x86_64 runtime without AVX2/FMA, versus `314c6bf`, I measured 
approximately +39%/+46%/+37%/+45% for rows 1/7/8/31; rows=32 was effectively 
unchanged, isolating the fixed transpose cost.
   
   Please restore a small-batch direct path when `score_argmin_kernel` resolves 
to scalar, use a backend-specific threshold, or cache the transposed codebook 
after training.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to