NEON-vectorized difference - #568
Open
gitRasheed wants to merge 3 commits into
Open
gitRasheed wants to merge 3 commits into
gitRasheed wants to merge 3 commits into
Conversation
union2by2 already has a generic definition and an arm64 one. Give difference, exclusiveUnion2by2, intersection2by2 and intersection2by2Cardinality the same split so vector versions can land one at a time without touching the shared files. The arm64 versions pass through to the scalar code for now.
This was referenced Sep 11, 2026
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.
NEON kernel for
difference, the array-container AndNot, on arm64. Both arrays need at least 16 elements. Smaller inputs keep the scalar merge.The compare core is the intersection kernel's. The new part is deferred emission. A set1 value with no match in the current set2 block may still match a later one, so match bits accumulate in a register and survivors are compacted and stored only when the set1 block retires. The output is always a subset of the set1 elements consumed so far and callers allocate
len(set1), so stores need no bounds clamp. When set1 runs out of whole blocks while a set2 block is still held, the kernel skips the held values at or below the retired set1 maximum before handing both tails to the scalar merge.CRoaring has an x86 version of this operation,
difference_vector16.differencewas the largest remaining scalar hotspot among the array set operations, 23 percent of AndNot pipeline time on the real-roaring-datasets corpus. Search indexes call AndNot once per query term to mask deleted documents, and deletion bitmaps are usually array containers.I made a page that steps through this kernel on worked examples and annotates the assembly line by line, to make the design choices easier to follow: https://claude.ai/code/artifact/5f77af0d-89c2-4965-9d15-95fde361c47f#andnot (the other tabs cover the other three kernels).
Changes
setutil_arm64.go: the lane-compaction table, its own commit, shared with NEON-vectorized intersection2by2 #543, NEON-vectorized union2by2 #567 and NEON-vectorized exclusiveUnion2by2 #569.setutil.go/setutil_generic.go:difference,exclusiveUnion2by2and the two intersection entry points get the generic/arm64 split thatunion2by2already has. This commit is shared with NEON-vectorized intersection2by2 #543 and NEON-vectorized exclusiveUnion2by2 #569 so the three merge in any order, and it drops out of the others once one of them lands; the two passthrough files for the other operations are part of it.setutil_andnot_neon_arm64.s: the kernel.setutil_andnot_arm64.go: dispatch and the spill drain for the in-place caller.iandNotArray, three-way self aliasing, duplicate-input store bounds, and one table of direct kernel exits covering every set2 tail length, every skip count, the mask carried into a fast-forward retire, and 65535 as a retired maximum.Testing
go testpasses on Graviton 4 with this branch, and passed on Graviton 5, 4 and 2 with the same kernel on 5 September.go fmtclean. Smat fuzzer, 300 seconds on Graviton 5: 375,187 executions, no failures.Performance
Against master on Graviton 5 (c9g.xlarge, Neoverse V3),
BenchmarkAndNotat a fixed iteration count, medians of five interleaved runs:Graviton 2 (c6g.large, Neoverse N1): 5.4x in place, 3.7x fresh.
The floor of 16 is where the kernel beats the scalar merge on every measured shape on Neoverse N1, V2 and V3 (sweep of sizes 8 to 96, both input orders, 128 rotated datasets per cell).