From fc56d68545dd44228d63e828d709fc1a9357bb65 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Sun, 4 Oct 2026 17:23:40 +0200 Subject: [PATCH] Sync with ndarray v0.2.2: three corrected passages Mirrors go-ndarray/ndarray#19: unary ufuncs are not all Map-based (Sqrt, Exp, Log, Log10 have their own kernels); Exp/Log are no longer listed at NumPy parity through Map, with measured Sin/Cos/Tanh figures instead; and the GEMM micro-kernels match the ikj oracle exactly only on integer data (CI has no qemu-x86_64 job). Co-Authored-By: Claude Opus 5.5 --- docs/performance.md | 11 +++++++---- docs/roadmap.md | 6 ++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/performance.md b/docs/performance.md index 637386a..f3c353b 100644 --- a/docs/performance.md +++ b/docs/performance.md @@ -4,7 +4,7 @@ Honest, reproducible head-to-head of `go-ndarray/ndarray` against **NumPy 2.2.4** on identical hardware. "On n'a pas le droit de se tromper": every number here is measured, every win is real, and where NumPy still leads it says so. -> **Where it stands at v0.2.1.** This page grew round by round: the sections +> **Where it stands at v0.2.2.** This page grew round by round: the sections > up to *Where go-ndarray still loses* were measured on an arm64 Tart VM > (4 vCPU, NumPy 2.2.4, OpenBLAS 0.3.29); the dated sections after it on an > AMD Zen 3, 16 cores, against **NumPy 2.5.3 + OpenBLAS 0.3.34**. On the Zen 3: @@ -336,7 +336,8 @@ The SIMD kernels are also validated against the scalar oracle per-arch in CI: | `Sqrt` | **FIXED — go wins 2.8× at 4 M, *Into* 1.8× small** | packed `SQRTPD` (amd64) / packed NEON `FSQRT V.2D` (arm64) / intrinsic `FSQRTD` (others), off a non-`func`-pointer seam | | `Max` / `Min` | **FIXED — go wins 1.4× at 4 M, ~4× small** | builtin-`max` NaN-propagating oracle + 4-accumulator reducer + amd64 `MAXPD`+NaN-scan | | `MatMul` vs tuned BLAS (OpenBLAS) | **FIXED — parity at n=1024 (~0.99×, ~203 GFLOP/s); 0.97× at n=512** | by-lane FMLA micro-kernel (`FMLA Vd.2D,Vn.2D,Vm.D[i]` via `WORD`) closed the prior 0.76× gap. Only small n=256 trails (0.67×) on per-call overhead, not throughput — see above | -| other `Map` ufuncs (`Exp`, `Log`, `Sin`…) | NumPy ~parity (libm-bound) | a packed `VEXP`/`VLOG` is libm-accuracy work; the math, not the dispatch, dominates here | +| `Exp`, `Log`, `Log10` | **FIXED in v0.2.0** — own kernels | ports of Arm's optimized-routines, 2–3.5× `math.Exp`/`math.Log`; see the Exp and Log sections below | +| `Sin`, `Cos`, `Tan`, … (through `Map`) | ~parity with NumPy | per element on one Zen 3 core: Go `math.Sin` 10.5 ns vs NumPy 10.4, `math.Cos` 11.1 vs 10.7, `math.Tanh` 16.5 vs 13.5 (2026-10-04); `Map`'s call per element adds 15–20% | ## Allocation: `Workspace` (2026-10-04) @@ -631,5 +632,7 @@ per-arch job regenerates the committed `.s`, fails if it is stale, vets (asmdecl), builds (cmd/asm encodes), and runs the bit/NaN-correctness suite. The multicore path and the packed/cache-blocked GEMM driver are architecture-independent; only the GEMM micro-kernel is per-arch (NEON 4×8 on -arm64, AVX2/FMA 6×8 or SSE2 on amd64, scalar 4×4 on the other four — all bit-identical to the -scalar ikj oracle, validated per-arch in CI incl. amd64 under qemu-x86_64). +arm64, AVX2/FMA 6×8 or SSE2 on amd64, scalar 4×4 on the other four), each held to +the scalar ikj oracle in CI: exactly on integer-valued data, within tolerance on +general data, since above KC the block partials regroup the sum and the FMA +kernels round once per step where the scalar oracle may round twice. diff --git a/docs/roadmap.md b/docs/roadmap.md index 3b34fe2..39f8e3b 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -85,8 +85,10 @@ suite (plus the per-arch CI jobs already wired in `.github/workflows/ci.yml`). - **More creation — DONE.** `Linspace(start,stop,num)` (endpoint inclusive, final sample pinned to stop), `Eye(n,m,k)` / `Identity(n)` (k-th diagonal, non-positive m defaults to square), and `Reshape` `-1` dimension inference. -- **Ufuncs — DONE.** Unary math via the `Map` seam (`Sqrt`/`Exp`/`Log`/`Log2`/ - `Log10`/`Sin`/`Cos`/`Tan`/`Floor`/`Ceil`/`Round`/`Square`/`Power`) and +- **Ufuncs — DONE.** Unary math (`Sqrt`/`Exp`/`Log`/`Log2`/`Log10`/`Sin`/`Cos`/ + `Tan`/`Floor`/`Ceil`/`Round`/`Square`/`Power`): `Sqrt` has a SIMD kernel, + `Exp`/`Log`/`Log10` run ports of Arm's optimized-routines (v0.2.0), and the + rest go through the `Map` seam over Go's `math`; and broadcasting comparisons returning 0/1 float masks (`Equal`/`NotEqual`/ `Greater`/`GreaterEqual`/`Less`/`LessEqual`) plus pairwise `Maximum`/`Minimum`. (`Round` is math.Round / half-away-from-zero, unlike numpy's banker's