Skip to content

arm64: NEON div and max/min, Go 1.27 mnemonics for sqrt/add/sub/mul; parallelFor findings - #30

Merged
tannevaled merged 4 commits into
mainfrom
arm64/vfsqrt-mnemonic
Oct 7, 2026
Merged

tannevaled merged 4 commits into
mainfrom
arm64/vfsqrt-mnemonic

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

What changes

  • What Go 1.27 can express. Go 1.27 (the floor since v0.5.0) names VFSQRT, VFADD, VFSUB, VFMUL, VFDIV, VFMAX and VFMIN on .D2. Each was assembled with go1.27.1 and checked against its disassembly. VFSQRT V0.D2, V0.D2 → 6ee1f800, the word the kernel emitted before. Go's order for the subtraction: VFSUB V1, V2, V0 gives V0 = V2 − V1.
  • sqrt: the 4 WORDs become VFSQRT, with identical machine code.
  • add/sub/mul: plain VFADD/VFSUB/VFMUL replace the exact-FMA-against-1.0 trick. Results are bit-identical, and the code is easier to read.
  • div (new): a NEON VFDIV kernel. Division was scalar on arm64; the README already claimed it was NEON.
  • max/min (new): a NEON VFMAX/VFMIN kernel with four D2 accumulators, used from 8 elements up; below that, the scalar oracle. reduce_unrolled.go is now excluded on arm64, where it would be dead code, which the coverage gate caught.
  • Still a WORD: the 16 indexed FMLA …D[i] of the GEMM. Go has no mnemonic for it yet (pending as CL 764224).

Tests

  • New test: TestMaxMinSIMDSpecialsInVectorLanes puts ±0 and ±Inf at every position of arrays of 8–40 elements, so they reach every vector lane and the tail. The old specials test had 6 elements and only reached the scalar path.
  • Sabotages, all caught: binary operands swapped (sub gives b−a), VFMIN used in maxNEON, and the fold dropping two accumulators.
  • Real Linux arm64 (X-Gene, cfarm185): the kernels suite passes, fence tests included. Coverage is 100% with -race. go vet is clean on arm64, amd64, ppc64le, loong64, riscv64 and 386.

Measured (one core, 8 interleaved rounds against v0.5.1, medians)

DivInto Max MulInto
Apple M 1.34–2.19× 1.87–2.24× 1.21× at 1 Ki, 1.00× above
X-Gene 1.00× 1.18–1.41× 1.08–1.35×

Sum, SqrtInto and AddInto do not change and measure 1.00×, which serves as the control.

Docs

  • perf.md: the arm64 paragraph claimed no vector sqrt, add or div. It is corrected, marked as a correction, and carries the table above.
  • perf.md, Dot: the "cache affinity, not verified" guess is replaced by a measurement on bare-metal POWER9. The Dot loss at 8 workers comes from parallelFor's fork/join (2.4 of 8 CPUs busy). Three replacement schedulers were tried. None ships: each fixes one worker count and breaks another. Keeping exactly the static chunks still lost, which refutes the affinity idea. The code is on experiment/parallel-for-scheduling.
  • README / plan: max/min are now SIMD on arm64 too.

Version: v0.5.2 (kernels and docs, no API change).

🤖 Generated with Claude Code

tannevaled and others added 4 commits October 7, 2026 12:52
…ls (Go 1.27)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…asurements replace the cache-affinity guess

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ers off arm64 (dead there)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit cbe4aea into main Oct 7, 2026
12 checks passed
@tannevaled
tannevaled deleted the arm64/vfsqrt-mnemonic branch October 7, 2026 11:04
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