Skip to content

Experimental FlyDSL GEMM backend for TE PyTorch (BF16/FP16/FP32/FP8/MXFP8) - #676

Open
aris134 wants to merge 72 commits into
devfrom
amartin/flydsl-gemm-integration
Open

Experimental FlyDSL GEMM backend for TE PyTorch (BF16/FP16/FP32/FP8/MXFP8)#676
aris134 wants to merge 72 commits into
devfrom
amartin/flydsl-gemm-integration

Conversation

@aris134

@aris134 aris134 commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR introduces an experimental FlyDSL GEMM backend for the Transformer Engine PyTorch frontend, following the integration approach established in #667. The initial implementation currently targets gfx950.

The implementation connects FlyDSL to the existing GEMM dispatch path and adds the infrastructure required to invoke FlyDSL kernels from Transformer Engine. FlyDSL is integrated as an optional PyTorch dependency for ROCm builds. When NVTE_USE_FLYDSL=1 is set during installation, setup.py adds the flydsl package to the Python installation requirements.

The supplied kernels are built around a common four-wave GEMM design modeled after the core scheduling and data-movement structure used by HipKittens-style kernels, with datatype- and layout-specific adaptations layered on top.

The backend remains opt-in at runtime and is selected with NVTE_USE_FLYDSL=1. FlyDSL modules are imported lazily only when this path is selected. When the variable is unset, the active device is unsupported, or a GEMM configuration is not supported by the FlyDSL backend, Transformer Engine continues to use the existing GEMM backend.

This PR provides preliminary FlyDSL GEMM support for FP32, FP16, BF16, tensor-wise FP8, mixed E4M3/E5M2 FP8 inputs, and MXFP8.

Supported Data Types

Data Type Functional Support Performance Results
FP32 Enabled Not targeted
FP16 Enabled Preliminary; rerun pending
BF16 Enabled Available
Mixed FP8 E4M3/E5M2 Enabled Available
MXFP8 Enabled Available

Validation

Validation was added at both the kernel and model-integration levels.

  • Added user-facing GEMM tests under tests/pytorch/flydsl_kernels/ covering:

    • FP32, FP16, and BF16 inputs
    • Tensor-wise FP8, including mixed E4M3/E5M2 input formats
    • MXFP8
    • TN, NN, and NT GEMM layouts
    • Comparison against both torch.matmul references and the existing Transformer Engine GEMM backend
    • Multidimensional FP8 inputs and flattening behavior
  • Added test_linear_accuracy_flydsl to tests/pytorch/test_numerics.py to validate the FlyDSL backend through the public Transformer Engine Linear module. The test compares the native and FlyDSL execution paths for:

    • Forward output
    • Input gradient (dgrad)
    • Weight gradient (wgrad)
    • FP32, FP16, and BF16 parameter types
    • Unquantized, tensor-wise FP8, and MXFP8 execution
    • Multiple batch sizes and model configurations

The model-level test uses identical parameters and inputs for the native and FlyDSL paths, resets FP8 state between executions, and verifies that unsupported FlyDSL configurations fall back through the existing Transformer Engine dispatch path.

Performance

All performance was measured across an 81-shape LLM GEMM suite covering Llama 2, Llama 3.1, Qwen 2.5, and Mistral dimensions at microbatch sizes 1, 2, and 4. The complete shape list and benchmark methodology are included in the attached benchmark script.

MXFP8

MXFP8 performance was evaluated against the existing TE HipKittens MXFP8 GEMM backend. All 81 MXFP8 configurations completed successfully with no skips or validation failures.

Metric Median Speedup Geometric Mean Wins
End-to-end forward 1.064x 1.042x 69 / 81
End-to-end backward 1.018x 1.000x 45 / 81
Forward GEMM 1.084x 1.122x 79 / 81
Dgrad GEMM 1.125x 1.185x 78 / 81
Wgrad GEMM 1.132x 1.177x 80 / 81
Combined backward GEMMs 1.128x 1.189x 80 / 81

The FlyDSL MXFP8 kernels outperform HipKittens on nearly the entire kernel-level sweep. Forward GEMM wins 79 of 81 shapes, while the combined dgrad and wgrad path wins 80 of 81 shapes. Median kernel-level speedups range from approximately 1.08x for forward GEMM to 1.13x for the backward GEMMs.

End-to-end results include quantization, tensor preparation, dispatch, and other framework overheads in addition to GEMM execution. Under this measurement, FlyDSL achieves a 1.064x median forward speedup and approximately matches HipKittens in backward by geometric mean, while retaining a 1.018x median backward speedup.

FP8

Tensor-wise FP8 performance was evaluated using the HYBRID DelayedScaling recipe against the default Transformer Engine GEMM backend across the same 81-shape LLM suite. All 81 configurations completed successfully with no skips or validation failures.

Metric Median Speedup Geometric Mean Wins
End-to-end forward 0.950x 0.978x 21 / 81
End-to-end backward 0.926x 0.924x 13 / 81
Forward GEMM 0.955x 0.996x 20 / 81
Dgrad GEMM 0.968x 0.976x 29 / 81
Wgrad GEMM 0.966x 0.995x 29 / 81
Combined backward GEMMs 0.968x 0.986x 27 / 81

The default Transformer Engine backend outperforms the FlyDSL FP8 implementation on most shapes. At the kernel level, however, FlyDSL remains close to baseline: forward GEMM and wgrad are approximately at parity by geometric mean, while dgrad and the combined backward GEMMs reach approximately 0.98x and 0.99x baseline performance, respectively.

End-to-end results include FP8 quantization, scaling-state updates, tensor preparation, dispatch, and other framework overheads in addition to GEMM execution. Under this measurement, FlyDSL reaches approximately 0.95x baseline performance for forward execution and 0.93x for backward execution.

BF16

BF16 performance was evaluated against the default Transformer Engine GEMM backend across the same 81-shape LLM suite. Of the 81 configurations, 78 completed successfully and 3 were skipped.

Metric Median Speedup Geometric Mean Wins
End-to-end forward 0.907x 0.915x 1 / 78
End-to-end backward 0.916x 0.903x 0 / 78
Forward GEMM 0.922x 0.928x 3 / 78
Dgrad GEMM 0.979x 0.981x 15 / 78
Wgrad GEMM 0.934x 0.933x 4 / 78
Combined backward GEMMs 0.958x 0.957x 5 / 78

The default Transformer Engine backend outperforms the FlyDSL BF16 implementation on most shapes. FlyDSL is closest to baseline on dgrad, reaching approximately 0.98x baseline performance by both median and geometric mean. Combined backward GEMM performance is approximately 0.96x baseline, while forward GEMM and wgrad reach approximately 0.92x and 0.93x baseline, respectively.

End-to-end results include framework overheads in addition to GEMM execution. Under this measurement, FlyDSL reaches approximately 0.91x baseline performance for both forward and backward execution.

The three skipped configurations were the largest shapes in the sweep:

  • Llama-3.1-405B MBS=4: M=32768, N=16384, K=53248
  • Qwen2.5-7B MBS=4: M=32768, N=37888, K=3584
  • Qwen2.5-72B MBS=4: M=32768, N=59136, K=8192

These cases failed before kernel execution while packing a FlyDSL launch argument into a signed 32-bit integer, producing:

struct.error: 'i' format requires -2147483648 <= number <= 2147483647

FP32

Functional. FP32 optimization was not a target for this PR.

Artifacts

Performance Scripts

benchmark_mxfp8_flydsl_vs_baseline_suite.py

benchmark_bf16_flydsl_vs_baseline_suite.py

benchmark_fp8_flydsl_vs_baseline_suite.py

@aris134 aris134 self-assigned this Jul 22, 2026
@aris134 aris134 changed the title Experimental FlyDSL GEMM backend for TE PyTorch (BF16/FP16/FP32/FP8/MXFP8) [wip] Experimental FlyDSL GEMM backend for TE PyTorch (BF16/FP16/FP32/FP8/MXFP8) Jul 22, 2026
@aris134 aris134 changed the title [wip] Experimental FlyDSL GEMM backend for TE PyTorch (BF16/FP16/FP32/FP8/MXFP8) [WIP] Experimental FlyDSL GEMM backend for TE PyTorch (BF16/FP16/FP32/FP8/MXFP8) Jul 22, 2026
@aris134 aris134 added the ci-level 2 CI test level 2 label Jul 22, 2026
@aris134 aris134 added ci-level 1 CI test level 1 and removed ci-level 2 CI test level 2 labels Jul 22, 2026
aris134 added 8 commits July 23, 2026 04:31
Add dedicated FlyDSL FP8 NN and NT kernels alongside the existing TN path.

* dispatch TN, NN, and NT to specialized kernels
* select matching TE rowwise or columnwise storage without copies
* preserve operand scales and independent FP8 dtypes
* derive M/N/K from each kernel’s physical layout
* preserve TE output shapes while flattening only for launch
* validate unsupported layouts and shapes for controlled fallback
Route all tensorwise FP8 layouts through the common FP8 GEMM core after wrapper-side storage backing selection.

Remove the redundant FP8 NN and NT kernel variants since columnwise FP8 storage already provides the required materialized transpose.
Replace the eager PyTorch MXFP8 scale-packing path with stride-aware
FlyDSL kernels that directly convert TE E8M0 scales into the HK/MFMA-ready
[K/128, dim] packed layout.

The previous implementation composed packing from arange, indexing,
casts, shifts, masks, ORs, transposes, and contiguous copies. PyTorch
lowered these into dozens of small GPU kernels around every GEMM, which
dominated end-to-end runtime despite the FlyDSL GEMMs themselves being
faster.

The new path:

- launches one fused scale-pack kernel per GEMM operand
- supports both rowwise and columnwise TE scale layouts
- consumes non-contiguous scale views using their actual strides
- eliminates the intermediate iteration-major scale tensor
- removes eager transpose/contiguous preparation from the scale path
- preserves the existing HK/MFMA-ready packed representation
aris134 and others added 5 commits August 11, 2026 15:56
Prune helpers carried over from the upstream FlyDSL utils but never used by
the TE kernels:

- fp8_gemm_utils.py: preshuffle_b, compute_global_linear_128x128, StoreC,
  Mfma16x16x128 (all definition-only), and the now-unused arith / T imports.
- fp8_gemm.py and fp32_gemm.py: the LDS_SYM_* / LDS_ALIAS_DOMAIN / SCOPE_IDS
  module constants, which were defined but never referenced.

(wait_barrier, G2STransposeLoader, make_fp32_inputs and the unused _divmod
were already removed in earlier refactors.) Pure deletion, no behavior change;
imports and test collection unaffected.

Co-Authored-By: Claude <noreply@anthropic.com>
The FP32 wrapper transposed operand B into [K, N] and fp32_matmul then
transposed it back to [N, K] before launching the core, a redundant
round-trip on the TN hot path. Drop the internal transpose so the core
consumes B as [N, K] directly, matching every other dtype backend, and
move all NN/NT layout normalization into the wrapper.

Co-Authored-By: Claude <noreply@anthropic.com>
MXFP8 scale-packing and matmul rejected unaligned shapes with plain
ValueError, which escapes general_gemm's FlyDSLUnsupportedError-only
fallback and crashes the GEMM. Convert the shape-dependent rejections
(qk % 4, dim % 64, k % scale-group-size) to FlyDSLUnsupportedError so an
untileable MXFP8 GEMM degrades to the C++ backend. Genuine caller
contract violations (dtype, device, stride, scale-shape mismatch) keep
raising ValueError/TypeError.

Co-Authored-By: Claude <noreply@anthropic.com>
Address three review items:

- Output-format rejections (non-contiguous output, unsupported output
  dtype) now raise FlyDSLUnsupportedError across all backends so an
  unservable-but-valid GEMM falls back to the C++ backend instead of
  crashing, matching the existing non-contiguous A/B behavior. MXFP8
  do_gemm gains an explicit output-contiguity guard so a non-contiguous
  D no longer silently receives a throwaway .contiguous() copy.
- Add tests/pytorch/flydsl_kernels/__init__.py so test_gemm.py gets a
  package-qualified module name and no longer collides under pytest's
  prepend import mode with triton_kernels/test_gemm.py.
- Silence pylint W0622 on the DSL-safe divmod/min helpers that
  intentionally shadow builtins.

Co-Authored-By: Claude <noreply@anthropic.com>
Verify that an MXFP8 shape FlyDSL cannot tile (M not a multiple of the
256-wide M tile) degrades to the C++ backend via general_gemm rather than
crashing, and that the fallen-back result is numerically correct. Uses an
M-tiling mismatch rather than an untileable K, since the C++ MXFP8 backend
shares FlyDSL's K%128 constraint and could not serve a K-based rejection.

Co-Authored-By: Claude <noreply@anthropic.com>
Comment thread build_tools/templates/_rocm_init.py
Comment thread ci/pytorch.sh Outdated
Comment thread transformer_engine/pytorch/flydsl_kernels/gemm/gemm_common_utils.py
Comment thread transformer_engine/pytorch/flydsl_kernels/gemm/gemm_wrappers.py Outdated
Comment thread setup.py Outdated
Comment thread transformer_engine/pytorch/flydsl_kernels/gemm/fp8_gemm.py
Comment thread transformer_engine/pytorch/flydsl_kernels/gemm/fp32_gemm.py
aris134 and others added 6 commits August 12, 2026 13:56
Two fixes to the FlyDSL test invocation in run_test_config:

- run_test_config runs once per fused-attn backend, so gating on
  check_mxfp8_supported re-spawned the availability subprocess up to 5
  times. Detect FlyDSL support once and cache it in _FLYDSL_SUPPORTED.
- FlyDSL covers fp32/fp16/bf16 and tensor-wise FP8 GEMMs, not just MXFP8.
  Gate on gfx950 -- the arch FlyDSL dispatch actually requires -- instead
  of MXFP8 availability, so the non-MXFP8 cases run too. NVTE_ROCM_ENABLE_MXFP8=1
  stays on the invocation so the MXFP8 cases still run where supported.

Co-Authored-By: Claude <noreply@anthropic.com>
FlyDSL dispatch is gated to gfx950 (CDNA4), which has no FNUZ hardware --
FNUZ is gfx94x-only. The fnuz torch dtypes therefore cannot occur on this
path, so drop the dead capability/fnuz branch and map the TE FP8 dtypes
directly to the OCP torch dtypes (e4m3fn / e5m2). Remove the now-unused
get_device_compute_capability import. TODO: add the FNUZ mapping when
gfx942 support lands in a future PR.

Co-Authored-By: Claude <noreply@anthropic.com>
Per maintainer review: the install_requires entry only takes effect for
source builds with NVTE_USE_FLYDSL=1 set at build time, so it never reaches
users of the release wheels it was meant to help. Treat FlyDSL like
FlashAttention -- an optional backend the user installs themselves. The
runtime already degrades gracefully: general_gemm catches ImportError
alongside FlyDSLUnsupportedError and falls back to the C++ backend with a
warning when flydsl is absent.

Co-Authored-By: Claude <noreply@anthropic.com>
Now that flydsl is user-installed rather than a declared dependency, a user
can set NVTE_USE_FLYDSL=1 without the package present. Previously this fell
back to the C++ backend silently unless the opt-in fallback-warning flag was
set. Split the dispatch except so a missing-package ImportError always warns
once (pointing at `pip install flydsl`) regardless of the flag, while the
FlyDSLUnsupportedError path keeps its existing opt-in warning for shapes and
configs FlyDSL genuinely cannot serve.

Co-Authored-By: Claude <noreply@anthropic.com>
This module carried only the verbatim FlyDSL SPDX/copyright line, but it is
AMD-adapted TE integration code (require_block_tiling/require_launch_size
raise the TE-local FlyDSLUnsupportedError to drive general_gemm fallback,
plus the int32-launch guard and gfx950 helpers). Add the AMD copyright and
"Adapted by AMD" provenance line to match fp16_gemm_utils.py/fp8_gemm_utils.py.

Co-Authored-By: Claude <noreply@anthropic.com>
Per review: match the ROCM_PATH resolution order used elsewhere in TE
(common/__init__.py, build_tools/utils.py::rocm_path). Honor an explicit
ROCM_PATH, then probe /opt/rocm/core and /opt/rocm, then fall back to the
rocm-sdk devel root. The previous code only checked bare /opt/rocm and
skipped the /opt/rocm/core layout that the ROCm wheels/Dockerfile use.

Co-Authored-By: Claude <noreply@anthropic.com>
@aris134
aris134 requested a review from ipanfilo August 12, 2026 15:15
Since flydsl is user-installed rather than a declared dependency, the
version expectation lived nowhere in the repo. Add _MIN_FLYDSL = (0, 3) in
flydsl_kernels/gemm/__init__.py as the single source of truth and check it
at import time; a missing or too-old package raises ImportError, which
general_gemm already catches to warn once and fall back to the default
backend. Generalize that warning to cover the too-old case.

Document the backend in README.rst: gfx950 is the targeted architecture for
now (gfx942 planned), requires flydsl >= 0.3.0 (install it yourself, like
flash-attention), enabled via NVTE_USE_FLYDSL=1.

Co-Authored-By: Claude <noreply@anthropic.com>
Comment thread ci/pytorch.sh Outdated
Comment thread tests/pytorch/flydsl_kernels/test_gemm.py Outdated
Comment thread tests/pytorch/flydsl_kernels/test_gemm.py Outdated
Comment thread tests/pytorch/flydsl_kernels/test_gemm.py Outdated
Comment thread tests/pytorch/flydsl_kernels/test_gemm.py Outdated
Comment thread tests/pytorch/test_numerics.py Outdated
Comment thread tests/pytorch/test_numerics.py
aris134 and others added 3 commits August 12, 2026 19:41
The FlyDSL suite already self-gates: a module-level skipif handles non-gfx950
and importorskip handles a missing flydsl package, so on unsupported archs it
collects-and-skips (exit 0). The shell-side check_flydsl_supported gate in
ci/pytorch.sh duplicated that gfx950 detection, so remove it and invoke the
suite directly. Also correct a stale comment in test_gemm.py: flydsl is now a
user-installed package, not present only when NVTE_USE_FLYDSL=1 at build time.

Co-Authored-By: Claude <noreply@anthropic.com>
The GELU_AUX_BIAS epilogue (fused GELU over A@B + bias, saving the
pre-activation aux) is implemented in every FlyDSL GEMM backend, but only
the mxfp8 path had a test. Add the matching regular (fp32/fp16/bf16) and
tensor-wise fp8 tests so every dtype variant that supports the epilogue is
exercised, mirroring test_flydsl_vs_pytorch_mxfp8_gelu_bias.

Co-Authored-By: Claude <noreply@anthropic.com>
Mirror the plain-GELU tests: compare the fused output against a no-epilogue
call_gemm and assert they differ, so a GELU_AUX_BIAS epilogue that silently
reverted to DEFAULT would fail instead of passing vacuously. Added to the
regular, fp8, and mxfp8 gelu_bias tests (the mxfp8 one predated the guard).

Co-Authored-By: Claude <noreply@anthropic.com>
@aris134
aris134 requested a review from sudhu2k August 12, 2026 20:37
aris134 and others added 2 commits August 13, 2026 15:17
…tegration

# Conflicts:
#	transformer_engine/pytorch/cpp_extensions/gemm.py
Parametrize test_linear_accuracy_flydsl over fp8_model_params so the
FlyDSL GEMM path is exercised with both fp8 autocast only and fp8 init +
fp8 autocast. Skip the fp8_model_params=True + non-fp8 combination.

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

@ipanfilo ipanfilo 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.

Now when #667 is landed, test_gemm.py can be moved and merged

aris134 and others added 2 commits August 21, 2026 13:12
Replace the separate NVTE_USE_GEMM_TRITON and NVTE_USE_FLYDSL env vars with
a single NVTE_GEMM_BACKEND selector (unset -> default C++/hipBLASLt, "TRITON",
"FLYDSL"; any other value raises ValueError). Add get_gemm_backend() to
utils.py and route cpp_extensions/gemm.py and quantization.py through it;
update gemm_wrapper.py messages to match.

Merge the Triton and FlyDSL test_gemm.py suites into tests/pytorch/gemm/,
gated at runtime by NVTE_GEMM_BACKEND so each family self-selects. Update the
parent conftest gfx942 skip logic, test_numerics.py, ci/pytorch.sh, and README
to the new var.

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

aris134 commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

Now when #667 is landed, test_gemm.py can be moved and merged

Done in 248dbb8

@aris134
aris134 requested a review from ipanfilo August 21, 2026 14:29
Comment thread transformer_engine/pytorch/flydsl_kernels/gemm/__init__.py
flydsl is pre-1.0, so a 0.4.x bump is expected to change the API. Add an
exclusive upper bound so only the 0.3.x series is accepted.

Co-Authored-By: Claude <noreply@anthropic.com>
@aris134
aris134 requested a review from matthiasdiener August 21, 2026 16:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-level 1 CI test level 1

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants