Repository navigation
Add an AVX cosine distance for float vectors - #364
Open
rich-murphey wants to merge 1 commit into
Open
rich-murphey wants to merge 1 commit into
rich-murphey wants to merge 1 commit into
Conversation
distance_cosine_float() had a NEON kernel but no AVX one, so x86 builds with SQLITE_VEC_ENABLE_AVX ran the scalar loop for cosine while L2 used l2_sqr_float_avx(). cosine_float_avx() takes 16 floats per step into two independent sets of dot, |a|^2 and |b|^2 accumulators, uses FMA when the build has it (__FMA__) and mul+add otherwise, and finishes the tail of fewer than 16 elements in scalar code. It is used for 16 or more dimensions. test_distance_cosine_float() now checks lengths around the 16-element block (15 to 389, odd tails included) against a double-precision reference.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
On x86 builds with
SQLITE_VEC_ENABLE_AVX, L2 distance usesl2_sqr_float_avx()but cosine distance still runs the scalar loop indistance_cosine_float(). NEON hascosine_float_neon(); AVX had no counterpart. For a cosinevec0table, this makes cosine queries the slow path on x86.What changed
cosine_float_avx(): 16 floats per step into two independent sets of dot, |a|² and |b|² accumulators, so the loop is not bound by FP-add latency. It uses_mm256_fmadd_pswhen the build has__FMA__and mul+add otherwise, so the existing-mavx -mavx2flags in the Makefile build it unchanged. The tail (fewer than 16 elements) is finished in scalar code, so every dimension count is handled.distance_cosine_float()dispatches to it for 16 or more dimensions, behind the existingSQLITE_VEC_ENABLE_AVXflag. The function signature and the scalar path are unchanged.test_distance_cosine_float()intests/test-unit.cchecks lengths 15, 16, 17, 31, 33, 47, 384 and 389 against a double-precision reference, within 1e-5. Dropping the tail loop makes it fail.Tests
make test-loadable: 239 passed, 66 skipped, on the default AVX build and on anOMIT_SIMD=1build; same counts as main.make test-unit: does not compile on main (Fix test-unit.c so the C unit tests compile and pass #305 fixes that). With Fix test-unit.c so the C unit tests compile and pass #305 merged locally, it passes on the AVX build, an AVX+FMA build (-mfma) and a scalar build.-Wall -Wextra.Benchmark
AMD EPYC 7351P (Zen 1), GCC,
-O3 -mavx -mavx2(Makefile defaults), pinned to one core with its SMT sibling idle. 304,971 random unit vectors,float[384], k = 400, median of 15 queries through the Pythonsqlite3module. The host had other load, so the end-to-end rows vary about ±10% between runs.distance_cosine_float()over all rows, in memoryvec0cosine KNN, k=400vec0cosine KNN, k=400, with #337's heapvec_distance_cosine()…ORDER BY…LIMIT 400With
-mfmathe kernel row is 34.2 ms. KNN results are identical to main (recall@400 = 1.000 on every build).On main,
vec0KNN time is mostly top-k selection, which is why the kernel's 3.5× shows up as 1.2× there. In an earlier measurement on the same machine and data shape inside an application, with #337's heap top-k, exactvec0KNN went from 194 ms to 87 ms (2.2×) and thevec_distance_cosinescan from 635 ms to 344 ms (1.8×);vec0with the existing AVX L2 was 92 ms there.Related
SQLITE_VEC_ENABLE_AVX, so it adds no new ISA requirement beyond what that flag already implies, and whatever Linux/macOS builds bake AVX/AVX2 based on the build machine's CPU, so 0.1.10-alpha wheels SIGILL on older x86_64 #302/Fix SIGILL on pre-AVX x86_64: function-level target attributes + runtime CPU detection #307 settles for selecting AVX at build or run time applies to it too.test_vec_distance_cosine_extreme_magnitudesfrom Sum cosine distances and vec_normalize() in double #338 fails on an AVX build, because this kernel sums infloat32asl2_sqr_float_avx()does and ascosine_float_neon()does on main. If Sum cosine distances and vec_normalize() in double #338 lands first, I can widen this kernel to__m256dsums the same way Sum cosine distances and vec_normalize() in double #338 does for NEON; if this lands first, Sum cosine distances and vec_normalize() in double #338 would need to cover the AVX kernel.