Skip to content

fix(norm,moe): restore cuDNN source tensor rank; correct MoE grouped-matmul graphs - #39

Merged
SamuelReeder merged 7 commits into
mainfrom
users/sareeder/fix-norm-graph-rank
Sep 3, 2026
Merged

fix(norm,moe): restore cuDNN source tensor rank; correct MoE grouped-matmul graphs#39
SamuelReeder merged 7 commits into
mainfrom
users/sareeder/fix-norm-graph-rank

Conversation

@SamuelReeder

@SamuelReeder SamuelReeder commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Summary

Three DVC workload fixes, landed on the same branch:

  1. norm (rank): restores cuDNN source tensor rank and float epsilon in
    Workloads/headline/norm.tar.gz, and migrates four unique fp16
    LayerNorm graphs out of the legacy cudnn_frontend.tar.gz sample
    suite into the headline set.
  2. moe: corrects Workloads/headline/moe.tar.gz and
    Workloads/microbench/cudnn_bench_moe.tar.gz MoE grouped-matmul
    graphs to match hipDNN's MoeGroupedMatmulAttributes node contract.
  3. norm (LayerNorm axis): corrects the one layer_norm config's
    scale/bias axis order so its backward graph passes hipDNN's real
    node validation, not just JSON schema validation.

Risk assessment

Low. All three changes update DVC-tracked workload archives and their
internal manifests only. No hipDNN or provider source code changes.

Testing summary

norm (rank)

  • tools/check_deserialize.py --level json: norm 34/34 valid, fail=0.
  • tools/check_deserialize.py --level json: cleaned legacy cuDNN tar 49/49 valid, fail=0.
  • Confirmed all norm tensor ranks are 4.
  • Confirmed norm epsilon tensors use [1,1,1,1] and float/Float32Value.
  • Confirmed norm contains 34 forward and backward graphs (19 fwd, 15 bwd)
    after the four unique synthetic fp16 LayerNorm cases were added.
  • Confirmed migrated LayerNorm files are absent from the legacy tarball.

moe

  • tools/check_deserialize.py --level opgraph (built hipDNN, from_json
    • validate + build_operation_graph): 191/191 graphs pass, fail=0.
  • Verified after packaging round-trip (extract from the committed tarballs).
  • Confirmed no MoE forward/backward nodes remain with rank-2 tensors,
    non-INT32 routing tensors, or a non-zero top_k outside SCATTER mode.

norm (LayerNorm axis)

  • Found via the new check-deserialize CI job (ci: check JSON-level graph deserialization on all DVC workloads #41): --level json
    (pure-Python schema check) passed 34/34 even before this fix, but
    --level opgraph (real hipdnn_frontend) failed 1/34 on the gpt3-175b
    backward graph -- exactly the gap that CI job exists to catch.
  • After the fix: tools/check_deserialize.py --level opgraph reports
    norm.tar.gz 34/34, and the full Workloads/ corpus is 8696/8696.
  • Verified after packaging round-trip.

All three fixes pushed to the DVC storage remote.

Testing checklist

  • Validate JSON graph deserialization (norm, legacy cuDNN tar).
  • Validate opgraph-level deserialization (norm, moe, cudnn_bench_moe).
  • Validate all three packaged tarballs after extraction.
  • Confirm the full Workloads/ corpus passes --level opgraph (8696/8696).
  • Run the applicability sweep on gfx942 and gfx950.

Technical changes

norm (rank)

  • Restore [N,C,1,1] activation and gradient tensors.
  • Restore [1,C,1,1] scale and bias tensors.
  • Restore [N,1,1,1] saved-statistic tensors.
  • Restore [1,1,1,1] epsilon tensors with float32 representation.
  • Add four unique synthetic fp16 LayerNorm forward graphs to the headline norm workload.
  • Remove those four migrated graphs from the legacy cuDNN frontend tarball.
  • Document source selection, migration, and corrections in both manifests.

moe

  • Reshaped tensors to hipDNN's required rank-3 convention
    ([1, T, K] / [E, K, N] / [E, 1, 1]).
  • Corrected first_token_offset from an E+1-length cumulative-boundary
    array to the E-length per-expert start-offset array hipDNN expects.
  • Zeroed top_k on NONE/GATHER mode nodes (only SCATTER uses it).
  • Removed a token_ks input incorrectly carried by GATHER-mode dgrad
    nodes (forbidden in that mode).
  • Corrected token_index/token_ks tensors to INT32.
  • Split graphs with a disconnected router-GEMM component into separate
    *__router.json files (hipDNN requires one connected component per graph).
  • Corrected SCATTER-mode output row count to the dispatched-token count.
  • Excluded 21 wgrad graphs whose token/doutput row counts mismatch
    (pre- vs post-scatter counts); the current backward node has no
    scatter/token_index support, so this isn't a shape fix. Documented as a
    known gap in the manifest.

norm (LayerNorm axis)

  • LayernormBackwardNode enforces a trailing-normalized-axis convention:
    with normalized_dim_count=1 the real (C) axis of a rank-matched
    ("one-padded") scale must be the last dim, not the second. The
    gpt3-175b config (the only layer_norm case; every other graph is
    rms_norm) shipped scale/bias/dscale/dbias as [1,C,1,1],
    the same axis order used for RMSNorm -- which has no such check.
  • Reshaped scale/bias/dscale/dbias to [1,1,1,C] for both the
    forward and backward gpt3-175b graphs. x/y/dx/dy/mean/
    inv_variance are unaffected; this validator only inspects scale's
    shape. Scoped to the one layer_norm config -- every rms_norm graph
    is untouched.

Regenerate Workloads/headline/norm.tar.gz from the latest main tarball while
restoring the tensor shapes used by cudnn-frontend/benchmark/norms/
benchmark_single_norm.py:

- activations and gradients: [N,C,1,1]
- scale/bias and their gradients: [1,C,1,1]
- saved RMSNorm/LayerNorm statistics: [N,1,1,1]
- epsilon: [1,1,1,1], float/Float32Value

The previous extraction squeezed trailing singleton dimensions to rank 2 and
used a double epsilon. HIP MLOPS norm plans require rank 4 or 5, and the
LayerNorm validator requires float epsilon. Keep the existing fp32
statistic/affine correction unchanged.

Updated norm/MANIFEST.md with source references, corrected layout, and the
reason for the regeneration.

Validation: tools/check_deserialize.py --level json reports files=30 ok=30
fail=0 after fresh tar extraction. All tensors in all 30 graphs are rank 4;
all epsilon tensors are float/Float32Value with dims [1,1,1,1].
Migrate four unique forward LayerNorm graphs from the legacy
cudnn_frontend.tar.gz sample suite into norm.tar.gz:

- fp16 8192x128
- fp16 2048x128
- fp16 4096x768
- fp16 4096x128

Exclude the legacy bf16 1024x12288 graph because it duplicates the
model-derived GPT-3 LayerNorm shape already in norm.tar.gz. Preserve the
legacy synthetic source metadata and add norm_type/pass annotations.

Normalize the migrated graphs to the source-compatible rank-four layout and
float32 epsilon representation. Expand norm.tar.gz from 30 to 34 graphs:
19 forward and 15 backward. Update MANIFEST.md with the selection and
provenance.

Validation: tools/check_deserialize.py --level json reports files=34 ok=34
fail=0 after a fresh tar extraction. DVC object pushed to storage.
Remove the four fp16 LayerNorm forward graphs migrated into
Workloads/headline/norm.tar.gz from Workloads/microbench/cudnn_frontend.tar.gz:

- 8192x128
- 2048x128
- 4096x768
- 4096x128

Keep the legacy bf16 1024x12288 LayerNorm sample because it was not migrated;
the headline workload already covers that shape with the model-derived GPT-3
case. Update the legacy MANIFEST totals from 53 to 49 graphs and document the
migration.

No remaining legacy graph content changed. Revalidated both tarballs:
- cudnn_frontend.tar.gz: 49/49, fail=0
- norm.tar.gz: 34/34, fail=0
All MoE graphs in moe.tar.gz and cudnn_bench_moe.tar.gz failed opgraph
deserialization. Root causes:

- Tensors were flattened; hipDNN's MoE node requires rank-3 shapes
  ([1, T, K] / [E, K, N] / [E, 1, 1]).
- first_token_offset used an E+1-length cumulative-boundary array;
  hipDNN expects the E-length per-expert start-offset array.
- top_k was set to the real routing value on NONE/GATHER nodes, but
  those modes require top_k == 0 (SCATTER only).
- GATHER-mode dgrad nodes carried a token_ks input inherited from
  their paired SCATTER forward node; GATHER forbids token_ks.
- token_index/token_ks used the activation dtype instead of INT32.
- Some graphs had a disconnected router-GEMM component; hipDNN
  requires one connected component per graph, so these were split
  into separate *__router.json files.
- SCATTER-mode output row count used the reduced final-token count;
  hipDNN's contract requires the dispatched-token count.

Excluded 21 wgrad graphs whose token/doutput row counts mismatch
(pre- vs post-scatter counts) -- MoeGroupedMatmulBwdAttributes has no
scatter/token_index support today, so this isn't fixable by reshaping.

Result: 191/191 graphs (165 moe.tar.gz + 26 cudnn_bench_moe.tar.gz)
pass from_json + validate + build_operation_graph.

@tvy-amd tvy-amd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Samuel! lgtm

@SamuelReeder SamuelReeder changed the title fix(norm): preserve cuDNN source tensor rank fix(norm,moe): restore cuDNN source tensor rank; correct MoE grouped-matmul graphs Sep 1, 2026
SamuelReeder added a commit that referenced this pull request Sep 1, 2026
--level json only exercises dnn_benchmarking's pure-Python GraphLoader,
which doesn't know hipDNN's real node contract (tensor rank, dtype,
connected-components, per-node field constraints). Verified against the
pre-fix moe.tar.gz blob: --level json passed 121/121 on graphs that
--level opgraph correctly failed 113/121 on -- the exact bug class
fixed in the moe.tar.gz PR. --level json stays as a fast pre-check, but
the opgraph check (real hipdnn_frontend, built via the same no-GPU path
as setup.yml's rocm-build job) is now the actual gate.

Known red: one pre-existing failure (norm/bwd GPT3 LayerNorm-backward
padding constraint, documented in applicability_results/STATUS.md) will
be fixed in the norm/moe DVC PR (#39), not here.
The one layer_norm config (gpt3-175b) shipped scale/bias/dscale/dbias as
[1,C,1,1], the same axis convention as every RMSNorm graph in this
package. LayernormBackwardNode enforces a trailing-normalized-axis
convention instead: with normalized_dim_count=1 it requires the real
(C) axis to be the last dim of scale, not the second. The old
combination failed opgraph-level backward validation with
"LayernormBackwardNode requires that one-padded scale (input) conforms
to the specified normalized_dim_count"; forward's node has no
equivalent check, so the forward graph passed anyway with the same
axis order.

Fixed by reshaping scale/bias/dscale/dbias to [1,1,1,C] for both the
forward and backward gpt3-175b graphs. x/y/dx/dy/mean/inv_variance are
unaffected. Scoped to the one layer_norm config; every rms_norm graph
is untouched.

tools/check_deserialize.py --level opgraph: norm.tar.gz 34/34 (was
33/34), and the full Workloads/ corpus is now 8696/8696.
--level opgraph only calls from_json/validate/build_operation_graph,
which assemble the backend graph descriptor from JSON and never touch
an engine. hipdnn.Handle() otherwise eagerly loads every engine plugin
on construction, and HIPBLASLT_ENGINE's plugin aborts hard with 'no
ROCm-capable device is detected' on GPU-less machines (e.g. CI
runners). Call set_engine_plugin_paths([], ABSOLUTE) before creating
the Handle to skip all engine plugin loading -- confirmed no
regression locally (8696/8696 still pass).

Also copies the check-deserialize workflow (from #41) onto this branch
temporarily so this PR gets its own CI run against the current fix
commits before #41 merges to main.
SamuelReeder added a commit that referenced this pull request Sep 1, 2026
@SamuelReeder
SamuelReeder marked this pull request as ready for review September 3, 2026 20:26
@SamuelReeder SamuelReeder self-assigned this Sep 3, 2026
@SamuelReeder
SamuelReeder merged commit 146fc02 into main Sep 3, 2026
12 checks passed
@SamuelReeder
SamuelReeder deleted the users/sareeder/fix-norm-graph-rank branch September 3, 2026 21:02
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.

2 participants