Skip to content

perf(solvers): zero-copy col-major QR native kernel + call-time GRADWAVE_EIGENSOLVER read - #469

Merged
wladerer merged 2 commits into
mainfrom
perf/native-colmajor-glue
Sep 10, 2026
Merged

perf(solvers): zero-copy col-major QR native kernel + call-time GRADWAVE_EIGENSOLVER read#469
wladerer merged 2 commits into
mainfrom
perf/native-colmajor-glue

Conversation

@wladerer

Copy link
Copy Markdown
Owner

What

Two changes to the native Davidson stack:

  1. Kernel cleanup (solvers/native/davidson_native.c) — zero-copy col-major QR (zgeqrf/zungqr run directly on the basis buffers, exploiting that a row-major (rows, m) block is byte-identical to the col-major (m, rows) matrix whose columns are the basis vectors — deletes 2 explicit + ~4 hidden LAPACKE transpose passes per orthonormalization), planar-double SIMD elementwise loops (kinetic, v_eff multiply, residual norms, Teter directions), and sphere-only scatter-buffer zeroing instead of a full (n+1)-point memset per band (m/n ≈ 0.06–0.1).
  2. GRADWAVE_EIGENSOLVER read at call time, not import time (scf/loop.py) — the import-frozen copy silently ignored os.environ set after import gradwave. This is not cosmetic: it silently degraded every "native" arm of a benchmark ladder to eager (identical wall to eager at every thread count was the tell). Tests now pin the override via monkeypatch.setenv to lock in the call-time contract.

Measured (asus, 22-core, same-harness A/B, per-solve spy verifying the native path engaged — zero fallbacks; identical F and iteration counts in every arm)

Al-4 conv 4³ FD and Al-1 fcc 8³ FD, ecut 30 Ry, full SCF wall:

arm eager old kernel (#467) this PR
Al-4 1-thread (batch) 93.0 s 54.0 s 53.7 s
Al-4 1-thread (retire) 34.0 s 34.7 s
Al-4 8-thread (retire) 7.7 s 7.5 s
Al-4 default threads 34.0 s 7.8 s 7.7 s
Al-1 1-thread (batch) 53.3 s 21.7 s 21.7 s
Al-1 1-thread (retire) 18.0 s 17.8 s
Al-1 8-thread (retire) 4.3 s 4.2 s
Al-1 default threads 19.8 s 4.4 s 4.2 s

Honest verdict: the kernel rewrite is an end-to-end wash vs the old kernel (≤2%, within noise). It ships because it is strictly cleaner (fewer memory passes, no LAPACKE hidden copies), never slower, and the measurement campaign it enabled corrects two records:

  • The probe-era claim that the native kernel had a 0.32–0.45× single-thread deficit does not hold end-to-end: the old kernel was already 1.7–2.5× eager per thread e2e (93.0→54.0 s, 53.3→21.7 s). The kernel-level probe number was apples-to-oranges with e2e wall.
  • The corrected single-process native headline (this harness): Al-4 34.0→7.5 s (4.5×), Al-1 19.8→4.2 s (4.7×) with one knob (eigensolver: davidson-native), identical free energies to all printed digits. Last night's "9.8 s" figure was harness-inflated.

External proxy note: VASP is not runnable on this infrastructure; QE (same box, same ONCV pseudos/ecut/k-mesh) remains the honest external reference — QE pw.x Al-4 best 0.6–2.3 s, so the small-cell gap is now ~3–8× (was ~2–9×).

8/8 native unit tests pass against the new .so; the fast tier passes on asus with the branch .so active.

🤖 Generated with Claude Code

https://claude.ai/code/session_018j6BBnPtBSjkrnASSc52mT

wladerer and others added 2 commits September 10, 2026 06:17
…ly scatter zeroing in the native Davidson kernel

The row-major (rows, m) basis block is byte-identical to the col-major
(m, rows) matrix whose columns are the basis vectors, so zgeqrf/zungqr
now run directly on the buffers (LAPACK_COL_MAJOR) — deleting the two
explicit transposes plus LAPACKE's hidden row-major transpose copies
(~6 full passes per orthonormalization). Hot elementwise loops (kinetic,
v_eff multiply, residual norms, Teter directions) move to planar double
views with omp simd. The per-band full-box memset becomes 2m sphere-slot
writes against a kept-zero scatter buffer (m/n ~ 0.06-0.1), with the
forward FFT landing in a separate gather box.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018j6BBnPtBSjkrnASSc52mT
The import-frozen copy silently ignored os.environ set after gradwave was
imported — an in-process solver override no-oped and a benchmark ladder's
"native" arms all ran eager. Tests now pin the override via the actual
environment (monkeypatch.setenv) to lock in the call-time contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018j6BBnPtBSjkrnASSc52mT
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