Skip to content

fix(dyt): use a 1D grid and tile small-width rows - #1617

Open
apullin wants to merge 1 commit into
linkedin:mainfrom
apullin:dyt-1d-grid-row-tiling
Open

apullin wants to merge 1 commit into
linkedin:mainfrom
apullin:dyt-1d-grid-row-tiling

Conversation

@apullin

@apullin apullin commented Nov 12, 2026

Copy link
Copy Markdown

Summary

Modification to the shape handling in the fused DyT kernels that gives
a major performance gain in my application: a small network with DyT,
as tested on an H100.

  • Both kernels placed rows on CUDA grid dimension 1, which holds at most
    65,535 blocks, so more than 65,535 rows failed to launch. Both kernels
    now use a 1D grid.
  • One program per row starves the GPU on narrow rows. Widths up to 256
    columns now process several rows per 4,096-element tile, exposed as a
    BLOCK_M autotune dimension; wider rows keep one row per program, and
    the backward kernel runs up to four persistent programs per SM on the
    row-tiled path.
  • Padded rows are now masked out after the arithmetic in the backward
    kernel: with infinite alpha, tanh(inf * 0) is NaN and masked loads
    alone let it poison the parameter gradients.
  • dalpha and dbeta now take their parameters' dtypes, like dgamma
    already did, instead of rounding float32 parameter gradients through
    x.dtype. The output keeps the x.dtype contract.

Because the autotuned BLOCK_N is not known before launch, the backward
buffers are sized for the worst-case config and zeroed, so unused
programs/column blocks contribute nothing to the reductions.

Testing Done

Hardware: H100 (80GB HBM3).

  • make checkstyle: passes.
  • make test: passes (5294 passed, 1146 skipped, 21 xfailed, including all 45 test/transformers/test_dyt.py cases).
  • make test-convergence: 169 passed, 10 skipped, 3 xfailed, 3 xpassed (1 unrelated failure in mini_qwen3_5_moe bf16 due to LigerExperts MoE non-determinism on H100, verified identical on main).

The forward and backward kernels placed rows on CUDA grid dimension 1,
which holds at most 65,535 blocks, so more than 65,535 rows failed to
launch. Both kernels now use a 1D grid.

Widths up to 256 columns process several rows per 4,096-element tile
as an autotune dimension, instead of one program per row, which
starves the GPU on narrow rows. Wider rows keep one row per program,
and the backward kernel runs up to four persistent programs per SM
on the row-tiled path.

Padded rows are now masked out after the arithmetic in the backward
kernel: with infinite alpha, tanh(inf * 0) is NaN and masked loads
alone let it poison the parameter gradients.

dalpha and dbeta now take their parameters' dtypes, like dgamma
already did, instead of rounding float32 parameter gradients
through x.dtype. The output keeps the x.dtype contract.
@apullin

apullin commented Nov 12, 2026

Copy link
Copy Markdown
Author

Oh, it looks like the grid-limit piece overlaps a bit with #1605 / #1607. This was derived independently in my own training perf work (Meta internal), and also delivers narrow-row tiling, gradient dtype/padding fixes, and full testing on H100 hardware. LMK if any adjustments should be made, though!

This branch has not been deployed

No deployments
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