diff --git a/docs/performance.md b/docs/performance.md index d9854e4..af8e757 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.8.0.** This page grew round by round: the sections +> **Where it stands at v0.9.0.** 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**, except the @@ -38,8 +38,8 @@ here is measured, every win is real, and where NumPy still leads it says so. > available on that host (see *loong64: LASX kernels*). > SIMD kernels exist on **amd64, arm64, ppc64le, loong64, riscv64 and s390x** (LASX, > RVV and the s390x vector facility used only when the CPU reports them); the -> 32-bit targets run the pure-Go reference code; the s390x kernels are validated -> under emulation only (see *SIMD coverage* at the end). +> 32-bit targets run the pure-Go reference code; the s390x kernels are measured +> on a real IBM z17 (see *SIMD coverage* at the end). ## How a pure-Go library can beat NumPy @@ -915,15 +915,37 @@ stay dynamic, so its slower efficiency cores are handled as before. - **s390x (vector facility)**, since v0.8.0, when AT_HWCAP reports it: sum, dot, sqrt and add/sub/mul/div, eight float64 per loop in four two-lane registers plus a scalar tail, every instruction a Go mnemonic. - max/min and the GEMM micro-kernel stay scalar (the vector max with NumPy's - NaN rule needs the z14, beyond the z13 Go supports). The operand order of + Since v0.9.0 the GEMM micro-kernel is a 4×8 tile: sixteen two-lane + accumulators, the B row in one `VLM`, each A value replicated with `VLREPG` + and fused in with `VFMADB`. max/min stay scalar: the vector max with NumPy's + NaN rule needs the z14, beyond the z13 Go supports. The operand order of `VFSDB`/`VFDDB`/`VFMADB` was **measured**, not read: each run once on known - inputs (`VFSDB V1, V2, V3` is V3 = V2 − V1). ⚠ **Validated under emulation - only** (Docker's qemu-s390x locally, qemu-s390x in CI): the whole suite, the - fence tests, and two sabotaged kernels that both failed. **Not measured**: - the LinuxONE host did not answer. AT_HWCAP is read in the machine's byte - order, which on big-endian s390x is not the little-endian the parser first - assumed. + inputs (`VFSDB V1, V2, V3` is V3 = V2 − V1). AT_HWCAP is read in the + machine's byte order, which on big-endian s390x is not the little-endian the + parser first assumed. + + v0.8.0 shipped these kernels validated under emulation only. Since then they + run on **real hardware**: an IBM z17 (Type 9175), through the LinuxONE + Community Cloud, as a z/VM 7.3.0 guest with 8 vCPUs in a shared LPAR. There + the whole suite passes, as do the fence tests (a kernel reading one element + too far faults), `TestParallelStress` (27,752 calls) and two sabotaged GEMM + and elementwise kernels, which fail as they should. Measured with `hostload` + (0% busy, 0% steal) and `benchstat`, 8 interleaved runs each, every + difference at p = 0.000 unless marked: + + | IBM z17, time vs the scalar code | 1 core | 8 cores | + |---|---|---| + | `Sum` | −85% to −87% | −82% to −87% | + | `AddInto` / `MulInto` | −44% to −61% | −36% to −60% | + | `DivInto` | −6% to −18% | −6% to −22% | + | `SqrtInto` | −2% to −11% | −5% to −9% at 1–16 Ki, ~ above | + | `MatVec` 1024² | −44% | −40% | + | `Dot` 2^20 | −11% | ~ (p = 0.08) | + | `MatMul` 1024² (v0.9.0 GEMM) | 340 → 70 ms (−79%) | 43 → 10 ms (−76%) | + | GEMM geomean, 11 shapes | −74% | −66% | + + `Exp`, which none of this touches, measured within ±0.3% on one core: the + control. - The 32-bit targets keep the validated scalar oracles, using the same four-accumulator max/min, direct sqrt loop, and a **scalar 4×4 GEMM micro-kernel** over the packed panels, and diff --git a/docs/security.md b/docs/security.md index 20c480f..0a5b538 100644 --- a/docs/security.md +++ b/docs/security.md @@ -1,6 +1,6 @@ # Security -> Mirrored from [`SECURITY.md`](https://github.com/go-ndarray/ndarray/blob/main/SECURITY.md) at v0.8.0. +> Mirrored from [`SECURITY.md`](https://github.com/go-ndarray/ndarray/blob/main/SECURITY.md) at v0.9.0. ## Reporting @@ -59,6 +59,28 @@ standard library's known vulnerabilities; `govulncheck ./...` reports none. ## Audit log +**2026-10-09 (v0.9.0).** + +- **The Go floor is 1.27.2.** At 1.27.1, `govulncheck` listed standard-library + vulnerabilities fixed in 1.27.2 (GO-2026-6604, -6612, -6613, -6617, among + others), none of them reachable from this library. At 1.27.2 it reports none, + for the library or for its generator module. +- **The loong64 miscompile is not fixed in 1.27.2.** An earlier entry said + golang/go#81000 was fixed in 1.27.2; it was not. The backport + (golang/go#81147) is on the 1.27.3 milestone. On real hardware (cfarm401, + Loongson 3C5000L), the upstream reproducer (`go-gfx/gfx` `./color`, + `TestSkimageLabToRGBOutOfGamut`) **FAILS** when built with go1.27.1 and with + go1.27.2 alike. The evidence that this library does not hit the pattern + stands as before: the suite and `FuzzOps` on that machine. Raise the floor + to 1.27.3 when it ships. +- **The s390x kernels now run on real hardware**: an IBM z17, through the + LinuxONE Community Cloud (`/proc/sysinfo`: Type 9175, a z/VM 7.3.0 guest + with 8 vCPUs in a shared LPAR). The whole suite and the fence tests pass, + and `sumVX` reading one element past the end faults (4 KiB pages). The new + 4×8 GEMM micro-kernel is held to the same checks: it is guarded by its + wrapper and fenced, and a sabotaged copy that drops tile row 3 fails + (`MatMul m=50 k=70 n=90 [270]: 0 != -249`). + **2026-10-08 (v0.8.0).** New s390x vector-facility kernels, held to the same guards (wrappers check each kernel's reach; the PROT_NONE fence tests now include s390x). ⚠ They were validated **under emulation only** (Docker's @@ -116,7 +138,9 @@ two defects. Both are fixed, each with a test that fails without the fix. - go1.27.0 and go1.27.1 miscompile one loong64 pattern ([golang/go#81000](https://github.com/golang/go/issues/81000): a byte-sized inlined result spilled with an 8-byte store, clobbering the stack slot next - to it), fixed in 1.27.2. On a real Loongson 3C5000L with LASX (cfarm401), + to it), ~~fixed in 1.27.2~~ — **wrong, corrected 2026-10-09**: the backport + (golang/go#81147) moved to the 1.27.3 milestone, and go1.27.2 still fails + the upstream reproducer on cfarm401 (see the v0.9.0 entry). On a real Loongson 3C5000L with LASX (cfarm401), built with go1.27.1, the whole suite passes, including the fence tests, and `FuzzOps` ran 52.7 million executions natively without a divergence from its oracle. That is evidence that this library does not hit the pattern, not