Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 33 additions & 11 deletions docs/performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
28 changes: 26 additions & 2 deletions docs/security.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading