Skip to content

adler32

CI coverage

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 += ΣbytesPSADBW (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), or VMLEB/VMLOB (s390x). On big-endian s390x, VL loads byte i of memory into lane i, and the result is bit-identical to hash/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-s1 carry), modelled with llvm-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-simd remains ~7% faster on native hardware despite the static model putting the two at parity. The residual gap is below what llvm-mca resolves — 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 to hash/adler32.
  • Go 1.26's simd/archsimd is 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 scalar hash/adler32 baseline 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.