Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Compile time: fewer kernel specialisations per (T, INT)

Open
#91 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
35/100
Issue type
Refactor
Clarity
Mostly clear
Activity status
Active
Tech stack
julia
Domain
performance

Research direction

Start with the measured hot spots in src/numeric/front.jl (_with_width_class), src/numeric/subtree.jl (_with_local_bytes) and src/solve/sweeps.jl to see how the Val branches force all class variants to compile on the CPU backend. Compare against the structure barrier introduced in PR #90 as the reference pattern for a non-allocating dynamic barrier, and use bench/compare.jl to check each flag's GPU cost before merging it into a runtime branch. Done means measurably fewer specialisations per (T, INT) and a shorter cold compile (see the timings in the issue) with no GPU regression — but the issue itself flags the levers as possibly unacceptable, so confirm scope with the maintainer first.

Written by the indexing model from the issue text.

Description

performance triaged

Context: PR #90 (CI time). After #87–#89 the test suite is dominated by compilation, not by tests. CI went from ~21 min to ~52 min. With the parallel runner and the CPU job on kkt it is ~34 min, and the floor is the cold compile of one worker.

Measurements (owner's machine, CPU backend unless noted)

  • First solver factorization through the API, per (T, INT): 42–84 s after #87–#89 (27–47 s before). With the Cholesky/LDLᵀ barrier from PR #90: 20–36 s. An LDLᵀ call afterwards costs ~28 s more.
  • Full CPU suite in one process: ~18.5 min, of which ~12 min is the cold compile (the first file pays it, the rest runs warm).
  • On kkt (CI) the per-worker cold compile is 25–31 min (slow per-core compile).
  • CUDA: test_numeric_ldlt takes 1155–1787 s on CI (351 s before #87–#89). This is GPU kernel compilation of the new LDLᵀ variants.
  • Splitting a test file by element type only partly helps, because part of the compile doesn't depend on T. test_numeric_cholesky_c: one element type alone takes 157 s, all four 168 s. test_numeric_ldlt: 814 s → 226 s per element type.

Where the specialisations come from

Kernels are specialised on T (4) × INT (2) and, in addition, on several compile-time classes chosen with explicit Val branches:

  • _with_width_class (src/numeric/front.jl): regime-B width classes W ∈ (8, 16, 32, 64), used by the Cholesky (front.jl) and LDLᵀ (ldlt.jl) fused kernels. The branches are static, so the launcher compiles all four variants with its caller, even if the matrix only has one width class.
  • _with_local_bytes (src/numeric/subtree.jl): 4 regime-A budget classes (SUBTREE_LOCAL_SIZES), Cholesky and LDLᵀ.
  • Further Vals in the LDLᵀ kernels: HERM, the packed/unpacked panel (_lt_pk(Val(W))), Val(ASM), Val(NL), Val(W*(W+1)÷2), and the three pivot passes Val(1..3).
  • Solve sweeps (src/solve/sweeps.jl): 3 solve kinds × sub/det/forward variants, also through explicit branches.
  • Dense implementations: :generic, :vendor, :ka, plus strided-batched variants.

On the CPU backend a KA kernel is an ordinary Julia function, and every statically reachable variant compiles with its caller. On GPUs a kernel compiles at its first launch, so only the variants actually launched are compiled. But each of them is a separate GPUCompiler/LLVM run per T, INT.

Possible levers (not sure any is acceptable)

  1. Keep @localmem sizes compile-time (required by KA), but don't compile unused classes on the CPU backend. For example, put a dynamic barrier at the class branch, like the structure barrier in PR #90. It must not allocate in the numeric phase: the argument tuples hold immutable Numeric/Symbolic, which get boxed. A mutable carrier or a precomputed launcher table could avoid that.
  2. Fewer classes. On the CPU backend, a single width class (64) and a single budget class would do. Cost: the CPU tests would no longer exercise the smaller-class code paths that the GPU uses.
  3. Merge the Val flags that only select small code paths (HERM, pivot pass, packed panel) into run-time branches where the GPU cost is negligible. That needs benchmarking per flag (bench/compare.jl).
  4. Test side: not every testset needs all four element types and both index types. That would change the test conventions in AGENTS.md, so it's the owner's decision.

Open question: whether 1–3 can be done without losing GPU performance. The width-class and local-memory specialisations are the core of the regime-A/B design (PLAN §2.4), so this may turn out not to be worth it.

Dominant language
Julia
Stars
3
Forks
0
Avg merge
3h 2m
Merged PRs (30d)
65

Getting set up

  • No Dockerfile or Docker Compose file
  • Has a pull request template
  • No contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from exanauts/SparseDirectSolver.jl

All issues in exanauts/SparseDirectSolver.jl

Similar issues

More Julia issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.