Skip to content

Add vector right-hand-side ldiv! dispatch - #47

Merged
ChrisRackauckas merged 1 commit into
JuliaSIMD:mainfrom
ChrisRackauckas-Claude:vector-ldiv
Aug 8, 2026
Merged

Add vector right-hand-side ldiv! dispatch#47
ChrisRackauckas merged 1 commit into
JuliaSIMD:mainfrom
ChrisRackauckas-Claude:vector-ldiv

Conversation

@ChrisRackauckas-Claude

Copy link
Copy Markdown

⚠️ Draft — please ignore until reviewed by @ChrisRackauckas.

Adds first-class vector right-hand-side ldiv! methods for all four wrapper types (2- and 3-arg, Val thread flag), prompted by the measurement in SciML/LinearSolve.jl#1161 that the TriangularSolve matrix kernels tie a hand-written scalar back-solve and beat getrs! for nrhs == 1 once the native upper kernel exists.

Design

No reshape, no new kernel: the left-division paths already work on transpose(A), and for a vector b the required 1×n transposed operand is just transpose(b) — a stack immutable, so the whole path stays zero-allocation (a reshape(b, :, 1) header would allocate). With M = 1 the drivers run their scalar row-remainder sweep.

That sweep beats BLAS trsv while the triangle is cache-resident but loses to trsv's blocked sweep beyond L2, so the methods defer to LinearAlgebra above VECTOR_RHS_CUTOFF = 128:

per-leg, min times, Float64, 1 BLAS thread (EPYC 7502 / AVX2, ns)
   n  |  trsv-U    TS-U   ratio |  trsv-unitL  TS-unitL  ratio
    8 |    229      129    1.78 |     190         80      2.38
   16 |    390      249    1.57 |     270        179      1.51
   32 |    709      480    1.48 |     529        399      1.33
   64 |   1390     1029    1.35 |    1100        850      1.29
  128 |   3290     2470    1.33 |    2760       2320      1.19
  --- cutoff: kernels above this line, LinearAlgebra below ---
  256 |   8760     8730    1.00 |    7820       7890      0.99
  512 |  29899    29940    1.00 |   28920      29250      0.99
 1000 |  97480   101980    0.96 |  100390      95179      1.05

(Without the cutoff the kernel path degrades to 0.44x at n = 512 — measured before adding it; the cutoff keeps every size at parity or better vs current behavior.)

Verification

  • Full suite TriangularSolve.jl | 149168 149168 pass (Julia 1.12.4), Aqua + ambiguities clean. New testset sweeps n ∈ {1..500} straddling the cutoff, all four wrappers, both Val flags, 2-/3-arg, Float32/64, packed-lu! parent, and asserts zero allocations on the kernel path.
  • Failing-before: on main, which(TriangularSolve.ldiv!, (UpperTriangular{Float64,Matrix{Float64}}, Vector{Float64}, Val{false})) is the catch-all; with this PR it is a native method (and the n ≤ 128 timings above are the behavior change).
  • Not verified: AVX-512/aarch64 (cutoff chosen from this machine's L2; it is conservative — the scalar sweep is ISA-independent while trsv improves with wider vectors, so the crossover should not move much upward).

Reviewer notes

  • The cutoff is a hardware-flavored constant; happy to make it Float32-specific or derive it from VectorizationBase.cache_size if you prefer.
  • rdiv! with a vector is not added — LinearAlgebra has no such method surface for triangular right-division, so there is nothing to mirror.
  • Independent of Inline the backward panel kernels into the driver #46 (disjoint file regions; either merges first without conflict).

🤖 Generated with Claude Code

Single right-hand sides previously fell through the catch-all to
LinearAlgebra (BLAS trsv). The left-division paths already operate on
transpose(A), and for a vector the required 1×n transposed form is just
transpose(b) — no reshape, no allocation — so the vector methods forward
into the same dispatch with M = 1, which runs the kernels' scalar
row-remainder. Measured against trsv (min times, EPYC 7502/AVX2,
Float64) that wins 1.2-3.3x up to n = 128 but loses once the triangle
falls out of L2 (0.44x at n = 512), so solves above VECTOR_RHS_CUTOFF =
128 keep the LinearAlgebra path; above the cutoff the ratio is parity
within noise.

Motivated by SciML/LinearSolve.jl#1161: with this, single-vector RFLU
backsolves can route both legs through TriangularSolve without the
reshape trick measured there.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
@codecov

codecov Bot commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.69%. Comparing base (e4d4714) to head (24abdb7).
⚠️ Report is 6 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #47      +/-   ##
==========================================
+ Coverage   94.62%   94.69%   +0.07%     
==========================================
  Files           1        1              
  Lines         837      849      +12     
==========================================
+ Hits          792      804      +12     
  Misses         45       45              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ChrisRackauckas
ChrisRackauckas marked this pull request as ready for review August 8, 2026 10:44
@ChrisRackauckas
ChrisRackauckas merged commit c73023d into JuliaSIMD:main Aug 8, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants