feat(musa): 26 training-hot-path mudnn kernels - #60
Merged
Conversation
Extends the table-driven mudnn codegen from 64 to 90 kernels (92 m.impl
lines), covering the ops a transformer training step actually spends its
time in. Every op measured faster than the cpu_fallback it replaces, from
8.6x (linear) to 204x (isinf) at 2048x2048; the fallback cost is D2H+H2D.
New categories, each verified numerically against CPU before being added:
binary_bw (6) sigmoid/tanh/silu/gelu/threshold/leaky_relu backward.
Operand order is undiscoverable from the API -- all 12
Binary::*_BW modes return status 0 in BOTH orders -- so
it was pinned by comparing numbers to CPU formulas.
ternary (4) addcmul, addcdiv, where.self, clamp. Ternary maps onto
aten 1:1 with Run(out, self, t1, t2).
addmm (4) addmm/baddbmm, following the vendor's three-branch shape
(torch_musa ops/Matmul.cpp): mudnn's MatMul is
alpha*A@B + beta*C + gamma*bias, and aten's beta rides on
*gamma* when self is a vector (there c aliases d). The
3-arg RunWithBiasAdd reads the output buffer, so the
4-arg form is used throughout. MatMul rejects any
non-contiguous operand, hence the .contiguous() calls.
unary (12) tan, round, mish, hardswish, hardsigmoid, isnan, isinf,
leaky_relu, elu, softplus, clamp_min, clamp_max.
Several mudnn setter names actively mislead and were corrected by probe:
CLIP is (alpha=lo, beta=hi) with beta defaulting to 0, so clamp_min must
set +inf explicitly; SOFTPLUS's SetAlpha carries aten's beta and SetBeta
carries aten's threshold; HARDSIGMOID is clamp(alpha*x+beta, 0, 1) with
both defaulting to 0, i.e. all-zeros unless configured.
Also fixes two pre-existing bugs in the Reduce templates, both present
since the backend landed and both found while verifying addmm backward:
1. mudnn writes only output[0] when the output still carries the reduced
axes as extent-1 dims (keepdim) AND the input is non-contiguous AND
the reduced axis is not last. Measured: (4,5) stride (0,1) over dim 0
into a [1,5] output leaves slots 1..4 untouched. Now reduces into the
squeezed shape and restores keepdim with a view.
2. mudnn's multi-dim Reduce ignores strides outright, reading the input
as if contiguous -- (4,5) stride (0,1) summed over all dims returns
210 (sum of 1..20) instead of 60 -- and SIGFPEs on a fully-broadcast
input. Single-dim reduces honour strides correctly in all 12 layouts
probed. Non-contiguous inputs are now materialized when reducing more
than one dim, which subsumes the old MudnnReduceWouldFault guard.
Both reached real code through bias gradients: autograd feeds addmm/conv
backward an expand()ed grad_output, so linear(x, w, b) produced a bias
gradient of [correct, 0, 0, ...] instead of [correct] * N.
Verification on 8x MTT S5000 / mudnn v3300, torch 2.10.0+cpu:
- 92 op/dtype cases vs CPU pass. The 3 fp16 addmm deltas are 1 ULP --
the device is exactly as far from an fp32 reference as CPU fp16 is.
- 168/168 reduce cases pass across 2D/3D/4D x contiguous, transposed,
all-0-stride, partial-broadcast, single- and multi-dim, both keepdims.
- Autograd end-to-end matches CPU for 10 activations and linear x/w/b.
- 5-step SGD training loop: loss curve bit-identical to CPU.
- pytest tests/unit tests/integration/ops: 344 passed / 209 skipped /
3 xpassed / 0 failed, matching the clean-HEAD baseline exactly.
- ACCELERATOR=cuda and =gcu still configure; only musa paths change.
Left on cpu_fallback deliberately: bitwise_*, gcd, lcm, logical_not have
no mudnn mode (authoritative counts from the header: Unary 67, Binary 38),
there is no HARDSWISH_BW/HARDSIGMOID_BW/SOFTPLUS_BW, and relu6 and
rsqrt_backward have no wrapper in register.inc (CompositeImplicitAutograd,
so they decompose anyway). Registering an op with no kernel behind it
fails the dispatcher's backend check, whereas leaving it unregistered
stays correct -- so anything unproven stays out of the table.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
zhaoyinglia
approved these changes
Aug 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Extends the MUSA backend's mudnn coverage by 26 operators, chosen for the training
hot path. Coverage goes 64 -> 90 kernels. This is a performance change: every one of
these ops previously reached
cpu_fallback, which round-trips the data D2H+H2D.All of it is table-driven through the existing
scripts/codegen_mudnn.py-- the twogates (symbol gate against
libmudnn.so, wrapper gate againstcsrc/aten/generated/register.inc) and the idempotent conf append are unchanged.Template count grows with call shape, not with op count, which is what makes the
batch cheap: 26 ops needed 14 new templates.
New ops, by shape:
tanroundmishhardswishhardsigmoidisnanisinfleaky_reluelusoftplusclampclamp_minclamp_maxlogical_xorfloor_divideaddcmuladdcdivwhere.selfaddmmbaddbmmsigmoid_backwardtanh_backwardsilu_backwardgelu_backwardthreshold_backwardleaky_relu_backwardThree things the mudnn headers actively mislead about
Each was pinned with a standalone C++ probe against
libmudnn.sov3300 (no torch),comparing to CPU formulas -- not assumed from the header:
Unary::CLIPis(alpha=lo, beta=hi)and beta defaults to 0, soclamp_minmust set beta to
+infexplicitly or everything above 0 is clipped away.Unary::SOFTPLUS's setters are inverted vs aten:SetAlphacarries aten'sbeta,SetBetacarries aten'sthreshold. With neither set it returnsinf.Unary::HARDSIGMOIDisclamp(alpha*x + beta, 0, 1)with both defaulting to 0,i.e. all-zeros unless configured. aten's is
alpha=1/6, beta=0.5.Binary's 12*_BWmodes return status 0 for either operand order, so only thenumbers reveal the convention:
SIGMOID_BW/TANH_BWtake(grad, output), whileSILU_BW/GELU_*_BW/LEAKY_RELU_BW/THRESHOLD_BWtake(grad, input).addmm has three branches
MatMulcomputesd = alpha*A@B + beta*C + gamma*bias, andRunWithBiasAddhas twoforms with different algebra. The 3-arg form reads the output buffer for its beta
term (unusable here -- it would read uninitialized memory), so this uses the 4-arg
form. Which coefficient carries aten's
betathen depends on the shape ofself:selfbetarides onRunWithBiasAdd(d, a, b, c, {})betaRunWithBiasAdd(d, a, b, d, bias)(c aliases d)gamma[M,1]Run, thenout.add_(self, beta)MatMul/BatchMatMulalso reject any non-contiguous tensor ("MatMulRun only supportcontiguous tensor"), 0-strided C included -- hence the
.contiguous()calls, and whya 1-D
selfroutes through the vectorbiasslot rather than beingexpanded.Ops deliberately left on cpu_fallback
Authoritative mode counts from the header are
Unary67 /Binary38, and theycontain no equivalent for
bitwise_and/or/xor/not,gcd,lcm,logical_not,HARDSWISH_BW,HARDSIGMOID_BWorSOFTPLUS_BW.relu6andrsqrt_backwardhaveno wrapper in
register.inc(both CompositeImplicitAutograd, so they decomposeanyway). Unregistered ops reach
cpu_fallbackand stay correct; registering an opwith no kernel behind it would instead fail the dispatcher's "backend not registered"
check -- so anything uncertain stayed out.
Test plan
Environment:
torch==2.10.0+cpu, 8x MTT S5000, built with--no-build-isolation.fp32 maxerr <= 2e-5. The addmm fp16 cases differ by 0.0039, which is 1 ULP at
magnitude ~5 (2^-8), not a miscompute.
*_BWmodes checked againsttorch.ops.aten.*_backward, plusend-to-end autograd through relu/gelu/silu/sigmoid/tanh/leaky_relu/elu/softplus/
hardsigmoid/mish and
linear's x/w/b gradients.pytest tests/gives 482 passed / 209 skipped / 3 xpassed, against a478-passed baseline -- passed only goes up. (The 4 failures are
tests/manual/metaxvendor tests AttributeError-ing on a MUSA box; the 9 errors are the known missing
transformers. Neither is touched by this change.)benchmarked ops are faster, from 3.66x (
linear) to 41x (addcmul);softplus35x,
gelu_backward31x,floor_divide23x. Nothing regressed, so nothing wasremoved from the table.
ruff==0.15.12check + format clean.backends/musa/,codegen_mudnn.py,backends_musa.conf), so cuda/gcu are unaffected by construction. Aconfigure-only check for them is not runnable on this box -- it has no CUDA
toolkit, so cmake stops at
CMakeDetermineCUDACompiler.Regenerating with
python scripts/codegen_mudnn.pyat this commit reproduces thechecked-in generated files exactly.
🤖 Generated with Claude Code