Read backward reductions freshest-last (closes most of the remaining gap) - #45
Conversation
The backward kernels' nmuladd reduction read the already-solved trailing columns with ascending nk, so its first loads hit the block the previous kernel call had just stored, stalling on stores still draining. Walking nk descending reads the oldest columns first and the freshest block last — the mirror of the forward kernels' ascending reduction, which also ends on its most recently written block. The down-counting loop also compiles to the same shared-scaled-index address form as the forward loop (two address updates per trip instead of six). Measured (EPYC 7502/AVX2, Float64, nrhs=8, min times), left-upper ldiv! vs left-lower at equal UNIT flag, after the chain fix in JuliaSIMD#44: n=48 1.15x -> 1.10x, n=64 1.11x -> 1.06x, n=128 1.06x -> 1.04x, n=256 1.05x -> 1.02x, n=1000 1.02x -> 1.00x. Vs OpenBLAS trsm the upper leg is now 2.0-2.9x. Forward kernels untouched. 0.2.2 was registered from a0118b0 (pre-JuliaSIMD#44), so the JuliaSIMD#44 and this-PR improvements ship as 0.2.3. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
|
Error while trying to register: Register Failed |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #45 +/- ##
==========================================
+ Coverage 94.55% 94.62% +0.06%
==========================================
Files 1 1
Lines 827 837 +10
==========================================
+ Hits 782 792 +10
Misses 45 45 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Round 3, pushed to this PR: the remaining fixed ~5 ns/kernel-call was hunted with a direct-call A/B harness (dispatch excluded, zero-trip reduction so only the straight-line body differs; reproducible 5.3±0.7 ns across sessions — not code-placement luck). Facts established: per-trip loop cost is now identical (2.60 vs 2.65 ns/trip), every FP opcode count in the two bodies matches exactly, the delta is +15 integer bookkeeping instructions + 2 extra callee-saved registers, Resolution: Final gap (min times, unit-vs-unit, nrhs=8): n=36 1.09x, n=48 1.06x, n=64 1.04x, n=128 1.02x, n≥256 1.00-1.01x — from 1.39x/1.30x/1.17x at n=48/64/128 when this started. Suite 148,781/148,781 green, sweep + packed-LU + alloc checks clean. |
Second (and, on this machine, final) round of closing the backward-vs-forward gap, following #44. Performance-only; forward kernels untouched; bumps version to 0.2.3 (0.2.2 was registered from a0118b0, before #44 merged).
What changed and why
Same theme as #44's fma-chain fix, but in memory rather than registers: the backward kernels' reduction over the already-solved trailing columns ran ascending nk, so its first loads hit the columns the previous kernel call had just stored — stalling on stores still draining from the store buffer. The forward kernels' ascending reduction naturally ends on their freshest block instead. Walking the backward reduction descending restores the freshest-last property.
A welcome side effect: the down-counting loop compiles to the same shared-scaled-index addressing as the forward loop (2 address-update instructions per trip instead of 6 — LLVM had been splitting the broadcast streams into independent pointer inductions in the ascending form; #44's zero-based re-basing alone had not fixed that).
Measured (EPYC 7502 / AVX2, Float64, nrhs = 8, min times, 1 BLAS thread)
Left-upper vs left-lower
ldiv!at equal UNIT flag (μs):The remaining ≤10% at n ≤ 64 is a ~5 ns/kernel-call fixed cost that shrinks under profiling instrumentation (i.e. at the measurement noise floor); I could not attribute it to any further structural asymmetry.
Left-upper
ldiv!(non-unit) vs OpenBLAStrsm:Verification
TriangularSolve.jl | 148781 148781pass (Julia 1.12.4), Aqua + ambiguities clean.Val(true/false), packedlu!parents to 512,BigFloatfallback): 0 failures; zero steady-state allocations.After merge this needs a
@JuliaRegistrator registerfrom an org member (as in #43) to ship 0.2.3.🤖 Generated with Claude Code