Skip to content

spec(igla): hslm_bwd_ptx -- HSLM backward dX and dW as int8 tensor-core PTX written by t27 (Closes #10847) - #10855

Merged
gHashTag merged 2 commits into
masterfrom
claude/hslm-ptx-backward-0d1f39
Oct 19, 2026
Merged

gHashTag merged 2 commits into
masterfrom
claude/hslm-ptx-backward-0d1f39

Conversation

@gHashTag

@gHashTag gHashTag commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner

Closes #10847

Two int8 tensor-core kernels for the HSLM backward pass (specs/ml/train_hslm.t27), written as PTX by a .t27 spec on the skeleton of the forward kernel of #10425:

  • hslm_dx_s8 (kernel 1): dX = dY x W^T with W ternary, in ternary_matmul_t_add's index layout (code j*K + i). One u32 of 16 codes per thread, decoded branch-free (nibble spread + prmt on the table 0x00FF0100), one 16-byte shared store.
  • hslm_dw_s8 (kernel 2): dW = X^T x dY summed over rows, outer_add's layout, i32 accumulation, k <= 131071.

The spec holds each kernel's integer CPU reference, the scales the host must use (dX per row, dW per column of X and dY), and f64 tests that bound the quantized path against an f64 reference. t27c ptx-run --kernel 1|2 runs them against that reference. The trainer is not touched; wiring is a follow-up.

t27c test-report specs/igla/coder/hslm_bwd_ptx.t27 on the lab: 19/19 pass, 0 vacuous, 7 invariants. The PTX text lengths are pinned to what ptx-run wrote on the pod (19431 and 20141 bytes). ptxas -arch=sm_89 -v: dX 98 registers, dW 99, 0 spills, 12288 B smem each.

Measured: RTX 4090, 2026-10-19

The pod was a RunPod RTX 4090 (driver 580.126.20, CUDA 12.4). It ran t27c --features cuda built from this branch and CUTLASS 3.5.1 cutlass_profiler built on the same pod. ptx-run used 200 iterations with every 31st row checked, plus a 3-iteration run checking every row. Both gave mismatches=0 on all 26 runs, kernel 0 (forward) included as a regression check.

About "torch" / cuBLAS: this is not a cuBLAS number. CUTLASS 3.5.1's profiler links cuBLAS (found and linked, ldd shows libcublas.so.12) but uses it only as a verification provider, never as a timed one. Writing a cuBLAS or torch harness would be hand-written C or Python, which this repo forbids. The comparison columns are therefore the best CUTLASS kernel per dtype and shape, which is the same class of kernel cuBLAS dispatches on sm_89, though not cuBLAS's own heuristic pick. f32 is CUTLASS SIMT sgemm, 128x128_8x2 tiles only (the filter kept the build short). That is torch's default f32 matmul path class, no TF32.

Shapes are padded to the kernels' multiple of 16: 243 -> 256 and 729 -> 736. CUTLASS bf16/s8 tn kernels also need M % 8 and K % 16, so they ran at rows 27 -> 32 and K 1782 -> 1792. f32 dX ran at the true 243/729 dims. GOP/s for t27 counts true HSLM work, 2 * rows * in * out.

dX = dY x W^T (rows x K -> rows x N), ms per call:

rows K x N (true) t27 hslm_dx_s8 GOP/s (true) CUTLASS s8 CUTLASS bf16 CUTLASS f32 SIMT
27 243 x 243 0.0057 559 0.0034 0.0053 0.0211
27 729 x 243 0.0134 714 0.0050 0.0081 0.0558
27 243 x 729 0.0058 1649 0.0052 0.0052 0.0213
1782 243 x 243 0.0071 29641 0.0045 0.0058 0.0220
1782 729 x 243 0.0156 40471 0.0065 0.0089 0.0566
1782 243 x 729 0.0086 73413 0.0080 0.0110 0.0237

dW = X^T x dY (M x N, summed over K rows), ms per call:

M x N (true) K rows t27 hslm_dw_s8 GOP/s (true) CUTLASS s8 CUTLASS bf16 CUTLASS f32 SIMT
243 x 243 27 0.0037 862 0.0038 0.0035 0.0062
243 x 729 27 0.0038 2517 0.0053 0.0050 0.0063
729 x 243 27 0.0037 2585 0.0053 0.0036 0.0063
243 x 243 1782 0.0748 2813 0.0066 0.0154 0.1353
243 x 729 1782 0.0753 8384 0.0068 0.0154 0.1316
729 x 243 1782 0.0746 8463 0.0068 0.0154 0.1340

What this says:

  • dX is 2.8-4.2x faster than f32 SIMT on every shape and within 1.1-2.7x of the best CUTLASS s8 kernel. On 1782 x 243 x 729 it beats CUTLASS bf16 (0.0086 vs 0.0110 ms), while reading a quarter of the weight bytes.
  • dW at K = 27 (one window) matches or beats CUTLASS s8 and is within 1.06x of bf16.
  • dW at K = 1782 (the batched step) is 1.8x faster than f32 SIMT but 11x slower than CUTLASS s8. A 243 x 729 output is only 4-12 blocks of 128 x 128 on a 128-SM part, and each block walks all 56 k-steps alone. The fix is split-K (or smaller tiles), and it is listed below as left. These numbers do not claim it.
  • The rows-27 calls are launch-bound (about 3-6 us) for every library, t27 included.

Raw output: ptx-run lines, the best-kernel table and the profiler CSVs are kept with the session notes.

Against specs/t27t/t27t.t27

  • RUNNER_USES_INT_MM stays false. The law is about gHashTag/igla-coder-gpu train.py calling torch._int_mm, and nothing here touches that runner.
  • F_MEASURED stays false. f is the share of a tern-tc-500m step spent in forward GEMMs. These are per-call times of backward HSLM GEMMs in isolation, not a step profile.
  • t27t.t27 is not edited.

Left (follow-ups)

  • Split-K (or a 64 x 64 tile) for dW at K = 1782, the one shape where this loses badly.
  • The elementwise backward kernels (RMSNorm, ReLU, softmax-CE) and the f32 -> int8 per-row/per-column quantize kernel. They do not fit this PR's PTX skeleton, which is GEMM-only.
  • Trainer wiring in specs/ml/train_hslm.t27: Trit -> 2-bit packing of W, padding to 256/736, scale bookkeeping, and the host calls.
  • A real cuBLAS or torch number needs either a CUTLASS version whose profiler times cuBLAS, or a t27-emitted cuBLAS call.

Foreign lines: +18 / -20. Two orphan .rs files that no mod reaches (neural/attention.rs, neural/hslm.rs) are deleted to pay the budget, and the orphan ceiling drops from 3 to 1. (The runtime_minimal stubs went in #10805 on master.)

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-10-19 05:19:11 UTC

Summary

Status Count
Total Open PRs 50
PRs with Failing Checks 44
PRs with All Checks Green 6
READY 2
FAILING 44
PENDING 0
NO CHECKS YET 0

These columns do not partition: 2 + 44 + 0 + 0 = 46, and there are 50 open PRs. A PR is being counted twice or not at all.

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=88b8cb73ceda != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

gHashTag and others added 2 commits October 19, 2026 12:52
… tensor-core PTX written by t27, with their CPU references (Closes #10847)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the RTX 4090 (Refs #10847)

dX 19431 bytes, dW 20141 bytes; both kernels gave mismatches=0 on every
HSLM shape at 27 and 1782 rows. Seal regenerated on the lab (19/19, 0 vacuous).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gHashTag
gHashTag force-pushed the claude/hslm-ptx-backward-0d1f39 branch from 5720ad3 to 0284b3a Compare October 19, 2026 05:53
@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-10-19 06:26:19 UTC

Summary

Status Count
Total Open PRs 50
PRs with Failing Checks 44
PRs with All Checks Green 6
READY 2
FAILING 44
PENDING 0
NO CHECKS YET 0

These columns do not partition: 2 + 44 + 0 + 0 = 46, and there are 50 open PRs. A PR is being counted twice or not at all.

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=88b8cb73ceda != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

@gHashTag gHashTag removed the no-auto-merge Opt out of the issue gate's default squash auto-merge (issue-gate.yml) label Oct 19, 2026
@gHashTag
gHashTag merged commit fc611e7 into master Oct 19, 2026
38 of 47 checks passed
gHashTag added a commit that referenced this pull request Oct 19, 2026
) (#10913)

* ledger(t27b): rows for six specs master carries unlisted, max_not_pass 129 -> 130 (Closes #10912)

As the steward applies UNLISTED (specs/tri/t27b/ledger_steward.t27): four pass rows (mos_psp103_50c_data, sar_adc, sar_comparator_psp, tri/worktrees/reap) and two blocked rows, ExprCall(undeclared fn), reason unimplemented (eval_hslm, ngram_hslm -- the same blocker hslm_corpus already carries). Verdicts are the UNLISTED lines of the t27b runs of #10908, #10855, #10907 and #10836 on current master. Not-pass rows become 129 blocked + 1 codegen = 130, so the cap rises by one, within the two new non-pass rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ledger(t27b): drop the eval_hslm row, max_not_pass back to 129 (Closes #10912)

The PR's own t27b run reported eval_hslm.t27 STALE: on current master the reference no longer compiles it (undeclared identifier 'b3_q' after a train_hslm change), and the steward lists only what the reference passes. Five rows remain (four pass, ngram_hslm blocked); not-pass rows are 128 blocked + 1 codegen = 129, the cap master had.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
gHashTag added a commit that referenced this pull request Oct 19, 2026
… specs use it (Closes #10947)

duplicate-bodies has been red on master since #10855: put_s, put_u, ins,
put_quad, emit_lds, emit_mma_step, emit_store, emit_epilogue, code_at, weight,
at_text and count_text were byte copies between ternary_gemm_ptx.t27 and
hslm_bwd_ptx.t27, in no ledger. They move to specs/igla/coder/ptx_emit.t27
(10 tests of its own); both kernel specs `use` it. Seals regenerated, gen-rust
output regenerated by t27c (it inlines the imported functions, so no Rust edit).
Existing tests: 21/21 and 19/19, 0 vacuous, unchanged. No ledger row added.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
gHashTag added a commit that referenced this pull request Oct 19, 2026
… specs use it (Closes #10947) (#10948)

duplicate-bodies has been red on master since #10855: put_s, put_u, ins,
put_quad, emit_lds, emit_mma_step, emit_store, emit_epilogue, code_at, weight,
at_text and count_text were byte copies between ternary_gemm_ptx.t27 and
hslm_bwd_ptx.t27, in no ledger. They move to specs/igla/coder/ptx_emit.t27
(10 tests of its own); both kernel specs `use` it. Seals regenerated, gen-rust
output regenerated by t27c (it inlines the imported functions, so no Rust edit).
Existing tests: 21/21 and 19/19, 0 vacuous, unchanged. No ledger row added.

Co-authored-by: Claude Sonnet 5.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.

HSLM backward on the GPU: dX and dW int8 tensor-core kernels written by a .t27 spec, measured against cuBLAS on an RTX 4090

1 participant