Skip to content

Numeric: division-free Cholesky column step in the front kernels - #125

Closed
sshin23 wants to merge 16 commits into
mainfrom
divfree-chol
Closed

sshin23 wants to merge 16 commits into
mainfrom
divfree-chol

Conversation

@sshin23

@sshin23 sshin23 commented Oct 10, 2026

Copy link
Copy Markdown
Contributor

What

The regime-A subtree kernel and the regime-B panel helper did the rank-1 trailing update with the unscaled column, dividing both factors by the pivot inside the inner loop — two f64 divisions per updated element (~1.2e9 per refactorization on the 78k-bus ACOPF condensed KKT), each ~10-20x the cost of the fused multiply-add that does the real work.

Both column steps are now scale-first: half one checks the pivot and scales the column by one reciprocal per work item (recomputed locally, so no extra barrier and no shared pivot cell); half two is a pure multiply-add trailing update and writes the diagonal. Same barrier count, same pivot sequence, same failure semantics.

Measured (GV100, 78k ACOPF condensed KKT, correctness-gated)

phase before after
regime-A subtree kernel 10.3 ms 6.2 ms
fused panel/mega phase 16.4 ms 11.6 ms
refactorization end-to-end 29.3 ms 25.7 ms

(cuDSS with its own reordering on the same device: 24.3 ms.)

Residuals unchanged; the arithmetic differs only in rounding ((a/d)(b/d) vs (a·rd)(b·rd)).

Tests

  • CPU suite: 9813 pass, all four element types (includes the reference-Cholesky elementwise checks).
  • CUDA: pending CI on this PR.

🤖 Generated with Claude Code

…ime-B front kernels

The fused subtree kernel and the regime-B panel helper performed the rank-1
trailing update with the UNSCALED column, dividing both factors by the pivot
inside the inner loop: two f64 divisions per updated element, which on the
78k-bus ACOPF condensed KKT (pglib_opf_case78484_epigrids, n = 674562) is
~1.2e9 divisions per refactorization, each an order of magnitude costlier
than the fused multiply-add.

Restructured both column steps as scale-first: the first half checks the
pivot and scales the column below the diagonal by one reciprocal (recomputed
per work item from local memory, so no extra barrier and no shared pivot
cell), the second half does the trailing rank-1 update with multiply-adds
only and writes the diagonal (the diagonal is untouched in the first half so
no work item reads a value another one is overwriting). Same barrier count,
same pivot sequence, same failure semantics (info = first non-positive
column, NaN included).

Measured on a GV100 (fused refactorization of the 78k KKT, correctness-gated
against the reference solve): regime-A subtree phase 10.3 -> 6.2 ms, fused
panel/mega phase 16.4 -> 11.6 ms, end-to-end refactorization 29.3 -> 25.7 ms
(cuDSS with its own reordering: 24.3 ms). Tests: full CPU suite 9813 pass
(all four element types); CUDA suite pending CI.
@sshin23

sshin23 commented Oct 10, 2026

Copy link
Copy Markdown
Contributor Author

Local CUDA validation (GV100, SDS_TEST_CPU=0 SDS_TEST_SKIP=test_aqua, test_numeric*/test_api/test_refactorization selections): 9517/9517 pass (all four element types), 33m. Together with the CPU suite's 9813 this covers both backends on the reference-Cholesky elementwise checks.

The rank-1 trailing-update loops of the regime-A and regime-B column steps
iterated the full r x r square and discarded the upper half with a guard,
wasting half the work items (divergent lanes idle through the local-memory
traffic of their warp). _tri_decode inverts the packed lower-triangle
enumeration (one float sqrt plus two exact fixups, verified exhaustively for
r = 1..700), so the loops now launch r(r+1)/2 work items with no guard.

GV100, 78k ACOPF condensed KKT: fused refactorization 25.3 -> 24.4 ms
(regime-A subtree phase 6.2 -> 5.1 ms); cuDSS with its own reordering on the
same device: 24.3 ms. CPU suite green (cholesky + refactorization
selections); CUDA suite pending.
One command on a clean NVIDIA box: installs Julia 1.13 via juliaup, clones
the branch, runs the CPU and CUDA test suites, then the end-to-end MadNLP
ACOPF solve (78k-bus pglib case) with cuDSS and with the SDS prototype
wrapper on the same device. SDS_BRANCH/SDS_DIR/SKIP_TESTS/CPU_ONLY_TESTS
knobs documented in the header.
@sshin23

sshin23 commented Oct 10, 2026

Copy link
Copy Markdown
Contributor Author

Second commit (packed-triangle enumeration, _tri_decode) also CUDA-validated locally: 9517/9517 pass (same GV100, same selections). Cumulative measured effect of the two commits on the 78k ACOPF condensed KKT fused refactorization: 29.3 → 24.4 ms, matching cuDSS (24.3 ms with its own reordering) on the same device.

sshin23 added 11 commits October 9, 2026 22:02
A readable alternative to fresh_machine.sh for the workflow itself: a single
julia file that bootstraps its own environment (registered packages plus this
repository at the tested rev), then reads as the workflow does — using
SparseDirectSolver, MadNLP, MadNLPGPU, ExaModels, ExaModelsPower; build the
78k-bus ACOPF; solve with cuDSS and with SDS on the same device. The shell
script remains for the full test-suite pass.
…ng T23

The previous commits copied subtree.jl/front.jl wholesale from a pre-T23
checkout, silently reverting the per-backend local-memory machinery (the
5-entry SUBTREE_LOCAL_SIZES and the matching _with_local_bytes), which made
every analysis fail with 'update _with_local_bytes' on a clean clone. Both
files are rebuilt from origin/main with only the two optimization hunks
applied (division-free scale-first column step; packed-triangle enumeration
of the trailing update).
…sion requires it to activate the GPU condensed-KKT path)
…hout a driver chose cuda on AMD nodes); document --startup-file=no everywhere
@sshin23

sshin23 commented Oct 10, 2026

Copy link
Copy Markdown
Contributor Author

Consolidated into #126 (single PR carrying #119, #122, #124, #125 plus the integration-branch extras, pre-gated on the GV100). Branch left in place.

@sshin23 sshin23 closed this Oct 10, 2026
sshin23 added a commit that referenced this pull request Oct 13, 2026
…ering knobs, division-free Cholesky, GV100 record (#126)

Device-built symbolic maps, the nd_nseps/nd_seed ordering parameters, the
division-free Cholesky column step, the fused factorization/solve pipeline
(src/fused.jl) with its no-pivot LDL mode and per-architecture tuning, the
MadNLP package extension (exported SDSSolver with sds_factorization = :ldl
and true inertia; no-pivot LDL disables 2x2 pivot-pair detection), the CPU
fast path and HSL reference benches, the fresh-machine @sds workflow, and
the campaign write-ups. Full validation record in the PR body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant