Experimental FlyDSL GEMM backend for TE PyTorch (BF16/FP16/FP32/FP8/MXFP8) - #676
Open
aris134 wants to merge 72 commits into
Open
Experimental FlyDSL GEMM backend for TE PyTorch (BF16/FP16/FP32/FP8/MXFP8)#676aris134 wants to merge 72 commits into
aris134 wants to merge 72 commits into
Conversation
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
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>
ipanfilo
requested changes
Aug 12, 2026
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>
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>
sudhu2k
requested changes
Aug 12, 2026
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>
…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>
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>
Contributor
Author
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>
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
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=1is set during installation,setup.pyadds theflydslpackage 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
Validation
Validation was added at both the kernel and model-integration levels.
Added user-facing GEMM tests under
tests/pytorch/flydsl_kernels/covering:torch.matmulreferences and the existing Transformer Engine GEMM backendAdded
test_linear_accuracy_flydsltotests/pytorch/test_numerics.pyto validate the FlyDSL backend through the public Transformer EngineLinearmodule. The test compares the native and FlyDSL execution paths for:dgrad)wgrad)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.
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
DelayedScalingrecipe 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.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.
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:
M=32768, N=16384, K=53248M=32768, N=37888, K=3584M=32768, N=59136, K=8192These cases failed before kernel execution while packing a FlyDSL launch argument into a signed 32-bit integer, producing:
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