Skip to content

Use AVX512_VPOPCNTDQ for the population-count kernels - #558

Merged
lemire merged 1 commit into
masterfrom
avx512-popcnt
Aug 27, 2026
Merged

Use AVX512_VPOPCNTDQ for the population-count kernels#558
lemire merged 1 commit into
masterfrom
avx512-popcnt

Conversation

@lemire

@lemire lemire commented Aug 27, 2026

Copy link
Copy Markdown
Member

Description

Adds AVX-512 counterparts to the five AVX2 population-count kernels, selected when the CPU has AVX512_VPOPCNTDQ.

Type of Change

  • Performance improvement

Changes Made

AVX2 has no vector population count, so popcnt_avx2_amd64.s builds one out of a VPSHUFB nibble lookup plus VPSADBW, handling 256 bits per iteration. AVX512_VPOPCNTDQ provides VPOPCNTQ, which counts all eight 64-bit lanes of a ZMM in a single instruction, so the new kernels handle 2048 bits per iteration: four ZMM loads, four VPOPCNTQ, four adds, with four accumulators to keep the adds off one dependency chain.

The five dispatchers in popcnt_avx2_amd64.go gain one branch each; no call sites change. CPUs without VPOPCNTQ keep the existing AVX2 path, and non-amd64 and appengine builds are untouched.

Detection is cpu.X86.HasAVX512VPOPCNTDQ from golang.org/x/sys/cpu, which already verifies that the OS saves the opmask and ZMM state and honors GODEBUG=cpu.avx512vpopcntdq=off to disable the path at run time. This makes golang.org/x/sys a direct dependency; it was already in the module graph.

Performance Impact

One 8 KB bitmap container (1024 words) on a Xeon Gold 6548N (Emerald Rapids), pinned with taskset, Go 1.26.3:

kernel pure Go AVX2 AVX-512 vs AVX2
popcntSlice 303.2 ns 179.9 ns 44.7 ns 4.02x
popcntAndSlice 449.0 ns 225.9 ns 68.5 ns 3.30x
popcntOrSlice - 226.9 ns 68.5 ns 3.31x

These back GetCardinality, computeCardinality, AndCardinality, OrCardinality, XorCardinality, Intersects, and the cardinality half of every bitmap-container boolean operation.

Testing

go test ./..., roaring64, the smat corpus and smat-hit checks, gofmt, go vet, and cross-builds for 386/arm/arm64 and -tags appengine, all on VPOPCNTQ hardware so the new path actually executes.

TestPopcntAVX512MatchesGo compares all five kernels against the portable implementations at lengths 0,1,2,3,4,5,7,8,15,16,17,31,32,33,63,64,65,1023,1024,1025 - covering the empty case, the sub-block cases, exact multiples of the 32-word main loop, and the scalar tail. TestPopcntDispatchMatchesGo checks the dispatchers themselves on any CPU.

Breaking Changes

None.

Additional Notes

Part of a series applying AVX-512 to roaring, following CRoaring's kernels. It overlaps with #557 and the follow-ups only in go.mod/go.sum (all add the same golang.org/x/sys requirement); the code is disjoint. Worth deciding as a group whether these should share one AVX-512 feature gate rather than one flag per feature - CRoaring gates on VBMI2 + VPOPCNTDQ + BITALG together.

https://claude.ai/code/session_0123ePBefqPjrCvhxdwWFakj

The AVX2 kernels build a population count out of a VPSHUFB nibble lookup
plus VPSADBW and handle 256 bits per iteration. AVX512_VPOPCNTDQ provides
VPOPCNTQ, which counts all eight 64-bit lanes of a ZMM register in one
instruction, so the new kernels handle 2048 bits per iteration with four
accumulators to keep the adds off a single dependency chain.

One 8 KB container on a Xeon Gold 6548N:

    popcntSlice      179.9 ns -> 44.7 ns  (4.02x vs AVX2)
    popcntAndSlice   225.9 ns -> 68.5 ns  (3.30x vs AVX2)
    popcntOrSlice    226.9 ns -> 68.5 ns  (3.31x vs AVX2)

These back GetCardinality, computeCardinality, AndCardinality,
OrCardinality, XorCardinality, Intersects, and the cardinality half of
every bitmap-container boolean operation.

Detection goes through golang.org/x/sys/cpu, so the OSXSAVE and XCR0
opmask/ZMM checks are handled upstream and GODEBUG=cpu.avx512vpopcntdq=off
disables the path at run time. CPUs without VPOPCNTQ keep the AVX2 path.

Claude-Session: https://claude.ai/code/session_0123ePBefqPjrCvhxdwWFakj
@lemire
lemire merged commit 7b2850c into master Aug 27, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant