You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
perf: one TurboQuant lookup implementation instead of six
The audit listed this as "the LUT backends are not actually vectorised - each one
gathers four scalar table lookups into a stack array and does a single vector
add". That was accurate, but the conclusion was wrong: there is nothing for SIMD
to do here. The scan is one table lookup per row, NEON has no gather instruction
at all, and measured on real data the loop already runs at about one lookup per
cycle. What the per-backend versions were actually buying was four parallel
float lanes instead of one serial double accumulator - and four independent
double accumulators buy the same parallelism without giving up the accuracy.
So the five copies collapse into one. Plain C, four accumulators, double
throughout. Measured against the NEON version on 4096 distinct rows at dim 768:
bits=2 25.60 -> 26.60 Mvec/s
bits=3 9.48 -> 9.55
bits=4 7.11 -> 7.00
End to end over 40k rows the three bit widths land within noise of where they
were; bits=2 may be a few percent slower, the run-to-run spread is wider than
the difference.
The point is the second number. Every backend now returns the same distance for
the same query: across 300 cases spanning all three bit widths and dimensions 64
to 1536, SIMD versus scalar divergence goes from 1.5e-4 relative to exactly
zero. The old spread came from accumulating in float over up to 384 terms while
the scalar path used double, so which distance you got depended on which CPU ran
the query - enough to reorder near-ties.
A third copy of the same loop lived in sqlite-vector.c as a fallback for a null
dispatch pointer that init_distance_functions() always sets. It is gone too.
Net 197 lines removed. What would actually make this scan faster is a different
storage layout - interleaving codes across vectors so the lookups become
in-register shuffles rather than memory gathers - which changes the on-disk
format and is not this change.
vector_turboquant_backend() keeps returning the same strings; API.md now
describes what it means, which is the SIMD tier selected at load time rather
than a TurboQuant-specific code path.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: API.md
+8-1Lines changed: 8 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -57,7 +57,14 @@ SELECT vector_backend();
57
57
**Returns:**`TEXT`
58
58
59
59
**Description:**
60
-
Returns the active backend used by TurboQuant lookup-table scans. This is useful when validating that TurboQuant is using the expected SIMD path on a target runtime.
60
+
Returns the SIMD tier selected at load time, the same one `vector_backend()` reports.
61
+
62
+
TurboQuant lookup-table scans no longer vary by backend: the scan is one table lookup
63
+
per row, which is already about one load per cycle on any machine, and NEON has no
64
+
gather instruction at all. A single implementation is used everywhere, so the same query
65
+
returns the same distance whatever the CPU — the per-backend versions this replaced
66
+
differed by up to 1.5e-4 relative because they accumulated in `float` while the scalar
0 commit comments