adler32¶
A drop-in fast path for the Adler-32 checksum, bit-for-bit identical to
hash/adler32. The bulk of the input is summed
by a SIMD kernel generated by go-asmgen;
the short tail reuses a hash/adler32-equivalent scalar loop. Pure Go,
CGO_ENABLED=0, stable Go. Repository →
API¶
sum := adler32.Checksum(data) // == hash/adler32.Checksum(data)
h := adler32.New() // hash.Hash32, == hash/adler32.New()
h.Write(data)
sum = h.Sum32()
| arch | kernel | notes |
|---|---|---|
| amd64 | SSE3/SSSE3 (2× unroll) + AVX2 (4× unroll, runtime dispatch) | PMADDUBSW weighted sum + PSADBW byte sum, deferred s1 carry |
| riscv64 | RVV (dispatch via HasV) |
length-agnostic VWMULU + VWREDSUMU; scalar fallback without V |
| arm64 | NEON on Go 1.27+, scalar on stable | needs integer VUMULL, upstreamed in Go 1.27 |
| ppc64le | VSX / AltiVec | VMULEUB/VMULOUB widening byte multiplies for the weighted sum; qemu-validated (power9), native perf pending |
| s390x | vector facility (big-endian; dispatch via HasVX) |
VSUMB byte sum + VMLEB/VMLOB weighted sum + VSUMQF reduce; scalar fallback without VX; measured on real IBM z15 (VXE2), 2026-07-03: ~5.4× vs scalar (-count=6) |
| loong64 / others | scalar | LSX kernel not yet shipped |
Algorithm¶
Adler-32 over d is s2<<16 | s1 with s1 = 1 + Σd[i] (mod 65521) and
s2 = Σ running-s1 (mod 65521). Following the classic zlib/Chromium SIMD
Adler-32, input is processed in chunks of at most nmax = 5552 bytes so the
16-bit lane sums cannot overflow before reduction. Per chunk:
s1 += Σbytes—PSADBW(amd64) /VWREDSUMU(riscv64) /VUADDLV(arm64) /VSUMB(s390x) / widening multiply-by-1 (ppc64le).s2 += chunkLen·s1_before + Σ weight_i·byte_i— the weighted sum is the SIMD core:PMADDUBSW(amd64),VWMULU(riscv64),VUMULL(arm64 NEON),VMULEUB/VMULOUB(ppc64le even/odd widening byte multiplies), orVMLEB/VMLOB(s390x). On big-endian s390x,VLloads byteiof memory into lanei, and the result is bit-identical tohash/adler32.
The chunkLen·s1 carry is folded as a vector shift-add on the running s1 and
reduced at the chunk end, where both mod 65521 reductions land at exactly the
same points as hash/adler32 — so the result is identical.
The arm64 / Go 1.27 VUMULL path¶
The weighted sum needs an integer vector multiply. Go's arm64 assembler
historically exposed only the polynomial VPMULL; the integer VMUL /
VUMULL / VUMLAL were upstreamed in Go 1.27. So stable Go (≤ 1.26) falls
back to scalar, and a //go:build go1.27 NEON kernel uses VUMULL — a concrete
demonstration of the new Go 1.27 integer-NEON multiply, validated on native arm64
with the gotip (1.27-devel) toolchain.
Performance¶
1 MiB random buffer, native amd64 (median MB/s; the meaningful figure is the per-run ratio, stable across four runners):
| implementation | kind | vs stdlib | vs mhr3 |
|---|---|---|---|
hash/adler32 (stdlib) |
scalar | 1.00× | — |
| this package | pure-Go SIMD (SSE3 + AVX2) | 12–14× | 0.93× |
mhr3/adler32-simd |
pure-Go SIMD (Chromium/zlib via gocc) | 13–15× | 1.00× |
Honest notes:
- The AVX2 kernel is unrolled 4× (128 bytes/iteration, deferred running-
s1carry), modelled withllvm-mca: 17.9 bytes/cycle (≈99% of the vector-ALU port ceiling) on the Zen3 model — up from 14.8 for the earlier loop; the 4× unroll lifts native throughput ~40%. mhr3/adler32-simdremains ~7% faster on native hardware despite the static model putting the two at parity. The residual gap is below whatllvm-mcaresolves — mhr3's software-pipelined kernel interacts with the real out-of-order frontend in a way the static analyzer idealizes away. So: near-parity, not a beat. Both are bit-identical tohash/adler32.- Go 1.26's
simd/archsimdis amd64-only; this package differentiates by being multi-arch (amd64 + riscv64 + arm64-on-1.27 + ppc64le + s390x) and Go 1.20+ compatible. - s390x: measured on real IBM z15 (VXE2), native execution, 2026-07-03,
-count=6: the big-endian vector-facility kernel (VSUMB/VSUMQF+VMLEB/VMLOB) runs at ~5.4× the scalarhash/adler32baseline on a 1 MiB buffer. - ppc64le: qemu-validated SIMD kernel (the VSX even/odd widening multiply); native throughput is pending (no POWER runner), so no ppc64le MB/s is quoted.
Coverage¶
100% of the Go code across native amd64, native arm64 (stable + gotip/go1.27 for
the NEON kernel), riscv64 under QEMU (RVV + no-V fallback), loong64 under QEMU,
and ppc64le (power9, the VSX kernel) + s390x (the big-endian vector kernel)
under QEMU with Force tests toggling hasVSX / VX. The .s kernels are validated
by differential tests against hash/adler32 plus fuzzing (on real AVX2; RVV / VSX
/ vector-facility under QEMU; NEON on native arm64). BSD-3-Clause.