NEON-vectorized intersection2by2 - #543
gitRasheed wants to merge 4 commits into
Conversation
b3938c9 to
73c73ad
Compare
|
btw, while working on this kernel, I noticed that Go's |
|
@gitRasheed Thanks. You are correct. I fixed this with a subsequent PR. |
|
@gitRasheed I assess all PRs manually, so it takes time. Sorry. |
|
@lemire no rush, just wanted to make sure I wasn't missing something, asking because I also have NEON AndNot PR ready but didn't want to spam open a bunch of PRs on you, fine if I send that over as well soon? |
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.
f5979ff to
38833d3
Compare
|
Rebased on current master and force-pushed. The description is rewritten for the new branch: shuffle table and dispatch split as separate commits, five rotations instead of seven in the match core, galloping from 32:1. Numbers are in the description. |
NEON implementations of
intersection2by2andintersection2by2Cardinalityfor arm64. Arrays where both sides have at least 16 elements run through a 128-bit SIMD kernel. Smaller or heavily skewed inputs keep the existing scalar code.NEON has no
cmpestrm(the gap noted in #288), so the kernel compares each set1 block against rotations of set2's block, reduces the result with a two-multiply movemask, and compacts matched lanes through the shuffle table. Blocks with no matches skip the mask extraction. A range gate fast-forwards disjoint runs on scalar boundary loads, and the galloping search takes over above a 32:1 size ratio. For the in-place caller, when output may have overwritten the current set1 block, the kernel hands it back in registers and the wrapper finishes from that copy. The booleanintersects2by2stays scalar because its early exit beats fixed vector work.Intersection had no vector path on any architecture here, and it is one of the top set-operation costs in real-roaring-datasets profiles.
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#intersection (the other tabs cover the other three kernels).
Changes
setutil_arm64.go: the lane-compaction table, its own commit, shared with NEON-vectorized union2by2 #567, NEON-vectorized difference #568 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 difference #568 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_intersect_neon_arm64.s: the two kernels.setutil_intersect_arm64.go: dispatch and the spill drain.iandArrayin-place aliasing geometry, three-way self-intersection, duplicate-input no-panic bounds, misalignment offsets, the galloping boundary at 32:1.BenchmarkIntersect2By2andBenchmarkIntersectCard2By2, rotating eight seeded datasets between iterations.TestAndSelfInPlacepinsBitmap.Andwith the same receiver.Testing
go testpasses for all packages on Graviton 4. The earlier version of the kernel also passed on Graviton 2 and Apple M1.go fmtclean. Smat fuzz (through the wrapper noted in #538): 585,864 executions, no failures.Performance
Graviton 4 (c8g.xlarge).
BenchmarkIntersect2By2from this PR, dispatch against scalar:The last commit (five rotations instead of seven, no-match exit, galloping from 32:1) on its own: dense 4096 materialization 2830 to 2658 ns, cardinality 2081 to 1953 ns, 32 against 2016 elements 558 to 354 ns. Real-roaring-datasets geomean over twelve datasets: And 15 percent and AndCardinality 36 percent faster than scalar as loaded, 8 and 17 percent after RunOptimize.
The floor of 16 is where every measured shape wins on Neoverse N1, V2 and Apple M1.