From 69679a619f422e64b3497d7814dc4dfe602839d0 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Sun, 18 Oct 2026 15:39:56 +0200 Subject: [PATCH] docs for ndarray v0.14.0: /docs/0.14/, float32 sum kernels, narrow integer sums - params.ndarray.version v0.14.0 (and baseURL). - sync-mirrors.sh at v0.14.0: performance gains the narrow-integer sums and the SSE2/NEON float32 sum blocks, measured on a Neoverse N1, a Zen 3 and the hosted runners. - security.md: SECURITY.md is unchanged since v0.13.0. Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- content/en/dtypes.md | 2 +- content/en/linalg.md | 4 +-- content/en/performance.md | 57 ++++++++++++++++++++++++++++++++++++--- content/en/random.md | 8 +++--- content/en/roadmap.md | 4 +-- content/en/security.md | 2 +- hugo.yaml | 4 +-- 8 files changed, 67 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 66afa43..80f3031 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ describes**: | Source | Published under | | --- | --- | -| branch `main`, with `params.ndarray.version: v0.13.0` in `hugo.yaml` | `https://go-ndarray.github.io/docs//`, here `/docs/0.13/` | +| branch `main`, with `params.ndarray.version: v0.14.0` in `hugo.yaml` | `https://go-ndarray.github.io/docs//`, here `/docs/0.14/` | | the newest version | also `https://go-ndarray.github.io/docs/latest/` | `https://go-ndarray.github.io/docs/` redirects to `latest/`. Each push to diff --git a/content/en/dtypes.md b/content/en/dtypes.md index a76f45c..0cc8d4c 100644 --- a/content/en/dtypes.md +++ b/content/en/dtypes.md @@ -8,7 +8,7 @@ description: "Why the dtype is a run-time property of one Array type, how arrays tags: [dtypes, numpy] --- -> Mirrored from [`docs/design-dtypes.md`](https://github.com/go-ndarray/ndarray/blob/v0.13.0/docs/design-dtypes.md) at v0.13.0. +> Mirrored from [`docs/design-dtypes.md`](https://github.com/go-ndarray/ndarray/blob/v0.14.0/docs/design-dtypes.md) at v0.14.0. > Status: implemented (Phase 2). Supersedes decision 4 of > [plan-ndarray.md](roadmap.md) ("`float64` first"). diff --git a/content/en/linalg.md b/content/en/linalg.md index ab4fc94..028acf0 100644 --- a/content/en/linalg.md +++ b/content/en/linalg.md @@ -8,9 +8,9 @@ description: "Solve, Inv, Det, Cholesky, QR, SVD, Eig and the rest of numpy.lina tags: [linalg, numpy] --- -> Mirrored from [`docs/linalg.md`](https://github.com/go-ndarray/ndarray/blob/v0.13.0/docs/linalg.md) at v0.13.0. +> Mirrored from [`docs/linalg.md`](https://github.com/go-ndarray/ndarray/blob/v0.14.0/docs/linalg.md) at v0.14.0. -Package [`linalg`](https://pkg.go.dev/github.com/go-ndarray/ndarray@v0.13.0/linalg) gives ndarray arrays the functions of +Package [`linalg`](https://pkg.go.dev/github.com/go-ndarray/ndarray@v0.14.0/linalg) gives ndarray arrays the functions of `numpy.linalg`, written in Go without cgo or assembly of its own: the matrix products it relies on are the ndarray package's packed SIMD GEMM. diff --git a/content/en/performance.md b/content/en/performance.md index 9d5edc0..05bfa30 100644 --- a/content/en/performance.md +++ b/content/en/performance.md @@ -880,9 +880,60 @@ float32 sum is also more accurate: on 2¹⁶ values of mixed sign and magnitude its error against the exact sum (`math/big`) fell from 75 to 0.87, and 2²⁴ followed by 1000 ones no longer saturates at 2²⁴ (`sum_pairwise_test.go`). -Left: float32 has no SIMD kernel yet (still 7× float64 for `Sum`), and the -integer sums convert the whole array to int64 before adding (NumPy's -accumulator type), an extra pass and allocation. +The narrow integer sums still converted the whole array to int64 (NumPy's +accumulator) before adding: an extra pass and 8 bytes an element. Since +v0.14.0 a sum or product of bools or integers narrower than 64 bits along a +contiguous axis widens each element as it is read (`foldWidened`, same +arithmetic, wrap-around included): **int32 `Sum` of 2²⁰ elements 2.872 → +1.548 ms (−46%)**, 2¹⁴ −51%, with float64, float32 and int64 unchanged as +controls (POWER9, same protocol, p = 0.002). + +### float32 sums on SSE2 and NEON (2026-10-18) + +NumPy keeps eight accumulators in its pairwise sum so that the inner loop can +be vectorised "without changing summation ordering". `kernels.AddBlocks8F32` +is that loop: blocks of eight float32 added lane by lane, one IEEE addition +per lane per block, in order. It is SSE2 on amd64 (`ADDPS` on two XMM +registers) and NEON on arm64 (`VFADD` on two `.S4` registers), and the scalar +loop elsewhere, so float32 sums are unchanged bit for bit. The kernel is +tested bit for bit against the scalar loop (NaN, the infinities, −0, +subnormals, overflow), and fenced with `PROT_NONE` pages on both operands. A +kernel adding from the wrong register fails the first test, and one reading +a block too many faults in the fence. + +Measured twice. First with the `bench` workflow on the hosted runners +(base and head alternated ten times in fresh processes on one thread, +`benchstat`, p ≤ 0.002), because the development Mac ran at a load of 97 and +most cfarm hosts refuse their keys since the host CA was revoked. Then on +real, idle hardware reached through a known_hosts file whose keys were +confirmed over a second network path: cfarm424 (Neoverse N1, 0.0% busy) and +cfarm420 (Zen 3, EPYC 7773X, 1.7% busy), same protocol, pinned to the idlest +CPU. On those, float32 `Sum` was −39% / −32% (N1) and −43% / −41% (Zen 3) at +2¹⁴ / 2²⁰. int32 was −15% / −11% on the N1, and on the Zen 3 within the +noise (±10% at that host's variance). The controls were unchanged. + +| vs v0.13.x | amd64 (EPYC 9V45) | arm64 (hosted) | +|---|---:|---:| +| float32 `Sum`, 2¹⁴ | −50% | −33% | +| float32 `Sum`, 2²⁰ | −49% | −43% | +| int32 `Sum`, 2¹⁴ | −12% | −12% | +| int32 `Sum`, 2²⁰ | −15% | −13% | +| float64, int64 `Sum` (controls) | ~ | ~ | + +The integer rows come from a defect the first run exposed. With the kernel +added, int32 `Sum` was **48–56% slower on amd64** although its code had not +changed: the disassembly of both binaries showed the same loop, byte for +byte, moved 224 bytes and now straddling a 64-byte line. The loop also +reloaded the operation from the stack every element. The widening folds now +use one loop per operation and four accumulators (exact: integer addition +and multiplication wrap modulo 2⁶⁴, so the order changes nothing). They also +consume the slice instead of indexing `i+1…i+3`, which had left four bounds +checks per iteration in the amd64 code; now there are none. + +Left: ppc64le, s390x, loong64 and riscv64 run the scalar block loop. ppc64le +needs `xvaddsp`, which Go's assembler lacks: go-asmgen/asmgen#27 adds the +VSX single-precision family to its registry, with references from GNU as +and llvm-mc. ## SIMD coverage diff --git a/content/en/random.md b/content/en/random.md index ef36a4a..f0a6c18 100644 --- a/content/en/random.md +++ b/content/en/random.md @@ -8,9 +8,9 @@ description: "numpy.random with NumPy's numbers for the same seed: the bit gener tags: [random, numpy] --- -> Mirrored from [`docs/random.md`](https://github.com/go-ndarray/ndarray/blob/v0.13.0/docs/random.md) at v0.13.0. +> Mirrored from [`docs/random.md`](https://github.com/go-ndarray/ndarray/blob/v0.14.0/docs/random.md) at v0.14.0. -Package [`random`](https://pkg.go.dev/github.com/go-ndarray/ndarray@v0.13.0/random) is `numpy.random` over go-ndarray arrays. Its goal +Package [`random`](https://pkg.go.dev/github.com/go-ndarray/ndarray@v0.14.0/random) is `numpy.random` over go-ndarray arrays. Its goal is migration: a notebook moved from NumPy to Go keeps its numbers for the same seed. @@ -24,7 +24,7 @@ u, _ := random.Rand(3) // np.random.rand(3) ``` `Example`, `ExampleSeed` and `ExampleGenerator_ChoiceN` in -[`example_test.go`](https://github.com/go-ndarray/ndarray/blob/v0.13.0/random/example_test.go) print NumPy's own numbers. +[`example_test.go`](https://github.com/go-ndarray/ndarray/blob/v0.14.0/random/example_test.go) print NumPy's own numbers. ## What is implemented @@ -118,7 +118,7 @@ random arguments. Used as-is, that made 3% of `np.random.randn` values and 10% of `lognormal` values one ULP off. So `random` rounds `log`, `log1p`, `exp`, `expm1` and `pow` **correctly** -([`libm.go`](https://github.com/go-ndarray/ndarray/blob/v0.13.0/random/libm.go)): a fast evaluation to about 2⁻⁶⁸ with Ziv's +([`libm.go`](https://github.com/go-ndarray/ndarray/blob/v0.14.0/random/libm.go)): a fast evaluation to about 2⁻⁶⁸ with Ziv's rounding test, and a double-double fallback (about 2⁻¹⁰⁰) for the one argument in ~2¹⁵ that needs it. Checked against mpmath at 300 bits on 63,000 arguments (0 misrounded; the committed `libm.json` keeps 8,400), and the fast paths diff --git a/content/en/roadmap.md b/content/en/roadmap.md index 8efaedc..f350d87 100644 --- a/content/en/roadmap.md +++ b/content/en/roadmap.md @@ -8,7 +8,7 @@ description: "The plan for go-ndarray and what ships today." tags: [roadmap] --- -> Mirrored from [`docs/plan-ndarray.md`](https://github.com/go-ndarray/ndarray/blob/v0.13.0/docs/plan-ndarray.md) at v0.13.0. +> Mirrored from [`docs/plan-ndarray.md`](https://github.com/go-ndarray/ndarray/blob/v0.14.0/docs/plan-ndarray.md) at v0.14.0. > Goal: a **pure-Go (CGO=0)** NumPy-style N-dimensional array library, as a > **standalone, reusable** module with correct scalar kernels now and a clean @@ -112,7 +112,7 @@ suite (plus the per-arch CI jobs already wired in `.github/workflows/ci.yml`). - **Performance — DONE (beats NumPy on its core ops; matmul at tuned-BLAS parity).** Several levers behind the kernel seam, measured honestly vs NumPy 2.2.4 (see [docs/perf.md](performance.md) and the repo's - [BENCHMARKS.md](https://github.com/go-ndarray/ndarray/blob/v0.13.0/BENCHMARKS.md)): + [BENCHMARKS.md](https://github.com/go-ndarray/ndarray/blob/v0.14.0/BENCHMARKS.md)): - **Multicore.** `Add/Sub/Mul/Div/Map`, `Sum/Max/Min`, the axis reductions, `Concatenate`/`Stack`, and the GEMM fan out across `GOMAXPROCS` above a size threshold (small arrays stay serial). Plus an allocation fix: the diff --git a/content/en/security.md b/content/en/security.md index fb0348d..a162ad7 100644 --- a/content/en/security.md +++ b/content/en/security.md @@ -8,7 +8,7 @@ description: "How to report a vulnerability, what the library guarantees, and th tags: [security] --- -> Mirrored from [`SECURITY.md`](https://github.com/go-ndarray/ndarray/blob/main/SECURITY.md) at v0.13.0. +> Mirrored from [`SECURITY.md`](https://github.com/go-ndarray/ndarray/blob/main/SECURITY.md) at v0.14.0. ## Reporting diff --git a/hugo.yaml b/hugo.yaml index 64684f8..b9e686b 100644 --- a/hugo.yaml +++ b/hugo.yaml @@ -6,7 +6,7 @@ # have used since the site was built with MkDocs and mike. The CI sets it # through --baseURL, from params.ndarray.version below. This default is the # same build. -baseURL: "https://go-ndarray.github.io/docs/0.13/" +baseURL: "https://go-ndarray.github.io/docs/0.14/" title: "go-ndarray" # themes/hextra is the tannevaled/hextra fork, pinned at a release tag: # upstream Hextra plus opt-in features (partial menu items, combos, @@ -97,7 +97,7 @@ params: # The ndarray release these pages describe. Its minor version is the # documentation's version label (v0.9.1 -> 0.9): the CI reads it here. ndarray: - version: "v0.13.0" + version: "v0.14.0" navbar: displayTitle: true