Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
c6544d0
[PyTorch] [torch.compile] torch.compile support for Linear
pggPL Aug 5, 2026
dfa79c2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 5, 2026
116d477
[PyTorch] Keep the broad-except pylint disable on the anchored line
pggPL Aug 5, 2026
dc4ff77
[PyTorch] [torch.compile] Style pass on the Linear custom-op path
pggPL Aug 5, 2026
250bd71
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 5, 2026
39d10f8
Address review: fix recompile assert, empty-batch sentinel collision,…
pggPL Aug 10, 2026
2c37350
Address review: simplify check_gemm_dims, trim test comments, restore…
pggPL Aug 10, 2026
eb3f50f
Shorten check_gemm_dims docstring
pggPL Aug 10, 2026
9695013
Drop tensor_can_be_materialized: inline an exact-class check in the t…
pggPL Aug 10, 2026
c64a0b0
Trim paraphrase comments in the Linear fake impls
pggPL Aug 10, 2026
b9bf693
Rename SP leading-dim helpers for direction clarity, trim two comments
pggPL Aug 10, 2026
aeb3481
Tighten custom_op module docstring intro
pggPL Aug 10, 2026
53b811b
Merge and simplify custom_op docstring paragraphs 2-3
pggPL Aug 10, 2026
948f94e
Keep the impl-vs-op contrast as two paragraphs
pggPL Aug 10, 2026
872387f
Drop reference to a PyTorch PR that will not land
pggPL Aug 10, 2026
dcc3c9a
Shorten _ensure_distributed_opaque_types docstring
pggPL Aug 10, 2026
d87eda6
Fall back to eager cleanly when ProcessGroup opaque registration is u…
pggPL Aug 10, 2026
1950585
Fix leftover 'priority order' wording at _FIELD_ADAPTERS
pggPL Aug 10, 2026
9c4fadf
Restructure register_custom_op docstring: caller contract first, drop…
pggPL Aug 10, 2026
a8e4fb7
Fix two fake/impl divergences found in multi-agent review
pggPL Aug 11, 2026
f47a637
Drop the global copyreg ProcessGroup reducer
pggPL Aug 11, 2026
23b093d
Skip use_compile numerics cases for DelayedScaling
pggPL Aug 11, 2026
e5a8ba8
Fix backward fake for UB reduce-scatter dgrad and extend the compiled…
pggPL Aug 11, 2026
2ff3301
Fall back to eager for DistributedWeight (GTP) under torch.compile
pggPL Aug 11, 2026
def2516
Widen two eager-fallback conditions
pggPL Aug 11, 2026
1d1ba9b
Defer the compile-disabled warning from import time to first compile use
pggPL Aug 11, 2026
1822afc
Log the compile-disabled reason at registration time (INFO, Transform…
pggPL Aug 11, 2026
dc5cecd
Release ctx.backward_objects after the compiled-op backward
pggPL Aug 11, 2026
6586d59
Mirror the save_original_input runtime flip in the forward fake
pggPL Aug 11, 2026
da11b9a
Fall back to eager for quantized input tensors under torch.compile
pggPL Aug 11, 2026
95d5271
Test hardening: exact eager-vs-compiled comparison, counters guard, r…
pggPL Aug 11, 2026
0e9e1be
Distributed tests: compare input gradients; exercise cudagraph replay…
pggPL Aug 11, 2026
e6b847e
Address review comments: re-drop the value-equality boilerplate, remo…
pggPL Aug 11, 2026
1a00946
Fix reduce-overhead UB warmup: mark step boundaries and drop grads be…
pggPL Aug 11, 2026
c095582
Carry ProcessGroup through the op boundary by c10d registry name
pggPL Aug 12, 2026
0dcaadd
Add train/eval mode-switch test as xfail
pggPL Aug 12, 2026
577b307
Remove dynamic=False from distributed compile runners
pggPL Aug 12, 2026
ff8bf74
Eagerly pre-allocate cuBLAS workspaces in Linear.reset_parameters
pggPL Aug 12, 2026
31b385e
[PyTorch] [torch.compile] Address trivial review comments
pggPL Aug 19, 2026
3bdce4e
Reference the upstream PyTorch fixes in the train/eval xfail
pggPL Aug 19, 2026
47fee78
Fold quantizers into the shared simple-value bundle
pggPL Aug 19, 2026
6815201
Make the eager-fallback warnings actually fire under torch.compile
pggPL Aug 19, 2026
db8c13c
Test the eager-fallback paths of the compiled Linear op
pggPL Aug 19, 2026
43a0783
Fold the compile-aware branching into the warn helper
pggPL Aug 19, 2026
0dc980a
Simplify adapter selection and drop the bundle's __getattr__
pggPL Aug 19, 2026
355a2cd
Trim OpaqueValueBundle to its actual consumers
pggPL Aug 19, 2026
d3f6849
Fold ProcessGroup fields into the shared simple-value bundle
pggPL Aug 19, 2026
1fe48cd
Merge remote-tracking branch 'upstream/main' into linear_torch_compil…
pggPL Aug 19, 2026
727def7
Reset FP8 global state between torch.compile tests
pggPL Aug 19, 2026
97825f3
Rewrite the custom-op arg boundary as a parsed plan
pggPL Aug 21, 2026
97bb4be
Add an output plan and lift the single-grad-output limit
pggPL Aug 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions tests/pytorch/distributed/run_layer_with_overlap.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
import torch
import torch.distributed as dist

try:
from torch._dynamo.utils import counters as dynamo_counters
except ImportError: # pragma: no cover
dynamo_counters = None

import transformer_engine.pytorch as te
from transformer_engine.common.recipe import (
DelayedScaling,
Expand Down Expand Up @@ -200,6 +205,19 @@ def _parse_args(argv=None, namespace=None):
parser.add_argument(
"--use-cuda-graphs", action="store_true", default=False, help="Use CUDA Graphs."
)
parser.add_argument(
"--compile",
action="store_true",
default=False,
help="Wrap each layer in torch.compile (tests Userbuffers on the compiled path).",
)
parser.add_argument(
"--compile-mode",
type=str,
default="default",
choices=["default", "reduce-overhead"],
help="torch.compile mode used when --compile is set.",
)
parser.add_argument(
"--ub-cfg", type=str, default=None, help="Optional TP config yaml file input."
)
Expand Down Expand Up @@ -285,6 +303,13 @@ def _parse_args(argv=None, namespace=None):
)
args = parser.parse_args(argv, namespace)

if args.compile and args.use_cuda_graphs:
parser.error(
"--compile and --use-cuda-graphs are mutually exclusive; to test"
" torch.compile with CUDA graphs use --compile --compile-mode"
" reduce-overhead."
)

if args.use_cuda_graphs and args.layer_type in [te.MultiheadAttention, te.TransformerLayer]:
warnings.warn(f"{args.layer_type.__name__} does not support CUDA Graphs!")
args.use_cuda_graphs = False
Expand Down Expand Up @@ -535,6 +560,14 @@ def run_fwd_bwd(model, x):
loss.backward()
return out

if opts.compile:
for i, layer in enumerate(test_model.layers):
test_model.layers[i] = torch.compile(layer, fullgraph=True, mode=opts.compile_mode)
dist_print(
f"Compiled test model layers with torch.compile (mode={opts.compile_mode})...",
debug=True,
)

torch_rng_state = torch.get_rng_state()
cuda_rng_state = torch.cuda.get_rng_state(torch.device(f"cuda:{LOCAL_RANK}"))
if opts.use_cuda_graphs:
Expand All @@ -545,7 +578,18 @@ def run_fwd_bwd(model, x):
if not opts.benchmark:
del test_graph
else:
if opts.compile and opts.compile_mode == "reduce-overhead":
# Warm up so the measured run below replays captured CUDA graphs.
for _ in range(2):
torch.compiler.cudagraph_mark_step_begin()
run_fwd_bwd(test_model, test_x)
test_model.zero_grad(set_to_none=True)
test_x.grad = None
torch.compiler.cudagraph_mark_step_begin()
test_out = run_fwd_bwd(test_model, test_x)
if opts.compile and opts.compile_mode == "reduce-overhead" and dynamo_counters is not None:
skips = dynamo_counters["inductor"]["cudagraph_skips"]
assert skips == 0, f"reduce-overhead fell back to eager: {skips} cudagraph skip(s)"
test_grads = [test_out, test_x.grad]
names = ["output", "input.grad"]
for test_name, test_param in test_model.named_parameters():
Expand Down
57 changes: 51 additions & 6 deletions tests/pytorch/distributed/run_numerics.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,15 @@ def _check_gradients(model_distributed, model_single, main_grad_check=False):
assert not bool(numerics_failed.item())


def _check_input_grads(input_single_node, input_distributed, parallel_mode, sequence_parallel):
grad_d = input_distributed.grad
if parallel_mode == "row":
grad_d = _gather(grad_d, dim=1)
elif sequence_parallel:
grad_d = _gather(grad_d, dim=0)
_check_outputs(input_single_node.grad, grad_d)


def _copy_params(model_distributed, model_single):
for dist_param, single_param in zip(model_distributed.parameters(), model_single.parameters()):
with torch.no_grad():
Expand All @@ -310,22 +319,35 @@ def _copy_params(model_distributed, model_single):


def _apply_models(
model_single_node, model_distributed, input_single_node, input_distributed, **kwargs
model_single_node,
model_distributed,
input_single_node,
input_distributed,
use_compile=False,
compile_mode="default",
**kwargs,
):
_alloc_main_grad(model_single_node, model_distributed) # for fuse_wgrad_accumulation=True
input_single_node.requires_grad_()
input_distributed.requires_grad_()
forward_single_node = model_single_node
forward_distributed = model_distributed
if use_compile:
# Reset the compile cache so parametrized cases don't trip recompile_limit.
torch._dynamo.reset()
forward_single_node = torch.compile(model_single_node, fullgraph=True, mode=compile_mode)
forward_distributed = torch.compile(model_distributed, fullgraph=True, mode=compile_mode)
with te.autocast(
enabled=QUANTIZATION is not None,
recipe=quantization_recipe(),
):
output_single_node = model_single_node(input_single_node, **kwargs)
output_single_node = forward_single_node(input_single_node, **kwargs)
with te.autocast(
enabled=QUANTIZATION is not None,
recipe=quantization_recipe(),
amax_reduction_group=NCCL_WORLD,
):
output_distributed = model_distributed(input_distributed, **kwargs)
output_distributed = forward_distributed(input_distributed, **kwargs)
return output_single_node, output_distributed


Expand Down Expand Up @@ -641,12 +663,20 @@ def test_quantized_all_gather():
# Linear #
############################################
@run_distributed_test()
def _test_linear(parallel_mode=None, sequence_parallel=False, **kwargs):
def _test_linear(
parallel_mode=None,
sequence_parallel=False,
use_compile=False,
compile_mode="default",
**kwargs,
):
"""Test the linear layer with specified parallel mode and sequence parallelization.

Args:
parallel_mode (str): 'row' or 'column' parallelism.
sequence_parallel (bool): Enable sequence parallelism if True.
use_compile (bool): Wrap the modules in ``torch.compile`` before running.
compile_mode (str): ``torch.compile`` mode ("default" or "reduce-overhead").
kwargs (dict): Additional arguments for the linear layer.
"""
# Set parameter data type
Expand Down Expand Up @@ -696,7 +726,12 @@ def _test_linear(parallel_mode=None, sequence_parallel=False, **kwargs):

# Apply models
output_single_node, output_distributed = _apply_models(
model_single_node, model_distributed, input_single_node, input_distributed
model_single_node,
model_distributed,
input_single_node,
input_distributed,
use_compile=use_compile,
compile_mode=compile_mode,
)

if "return_bias" in kwargs:
Expand Down Expand Up @@ -728,6 +763,8 @@ def _test_linear(parallel_mode=None, sequence_parallel=False, **kwargs):
main_grad_check=("fuse_wgrad_accumulation" in kwargs),
)

_check_input_grads(input_single_node, input_distributed, parallel_mode, sequence_parallel)


def test_linear():
"""Run linear layer tests with various configurations."""
Expand All @@ -740,12 +777,20 @@ def test_linear():
{"params_dtype": torch.float16 if QUANTIZATION != "nvfp4" else torch.bfloat16},
{"delay_wgrad_compute": True},
{"save_original_input": True},
{"use_compile": True},
{"use_compile": True, "compile_mode": "reduce-overhead"},
]

for kwargs in kwargs_list:
if kwargs.get("save_original_input", False) and QUANTIZATION == "fp8":
continue
if kwargs.get("delay_wgrad_compute", False) and NVTE_TEST_NVINSPECT_ENABLED:
# use_compile: debug instrumentation forces the eager fallback, so
# compile is a no-op there.
Comment thread
pggPL marked this conversation as resolved.
if NVTE_TEST_NVINSPECT_ENABLED and (
kwargs.get("delay_wgrad_compute", False) or kwargs.get("use_compile", False)
):
continue
if kwargs.get("use_compile", False) and QUANTIZATION == "fp8":
continue
for parallel_mode in ["column", "row"]:
for sequence_parallel in [False, True]:
Expand Down
45 changes: 45 additions & 0 deletions tests/pytorch/distributed/test_comm_gemm_overlap.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ def _run_layer_with_overlap(
quantization,
num_layers=1,
use_cublasmp=False,
use_compile=False,
compile_mode="default",
):
test_path = TEST_ROOT / "run_layer_with_overlap.py"
test_cmd = LAUNCH_CMD + [
Expand All @@ -129,6 +131,10 @@ def _run_layer_with_overlap(
if overlap_rs_dgrad:
test_cmd.append("--overlap-rs-dgrad")

if use_compile:
test_cmd.append("--compile")
test_cmd.append(f"--compile-mode={compile_mode}")

if fp8:
if quantization in ("fp8_delayed_scaling", "fp8_current_scaling") and not fp8_available:
pytest.skip(reason_for_no_fp8)
Expand Down Expand Up @@ -281,6 +287,45 @@ def test_layers_with_overlap_bf16(
)


@pytest.mark.parametrize("compile_mode", ["default", "reduce-overhead"])

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Kind of a general comment, but do we expect to ever see a case that would work under reduce
overhead mode but not work under the default mode? If so then maybe we could just test the stricter
mode if things are supposed to work under both of them?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The benefit of such approach would be time saved. If torch.compile + TE CI time will be big we may do that.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So did you measure the time increase of the CI due to this PR?

@pggPL pggPL Aug 19, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

1.5 min for L0(not just this PR, all torch.compile tests), ~5min for L1

@pytest.mark.parametrize(
"quantization",
[None, "fp8_current_scaling", "mxfp8"],
ids=["bf16", "fp8_current_scaling", "mxfp8"],
)
@pytest.mark.parametrize(
"linear_parallel_mode,overlap_rs_dgrad",
[
("row", False),
("column", False),
("column", True),
],
ids=[
"ROW-PARALLEL",
"COL-PARALLEL - BULK DGRAD/WGRAD",
"COL-PARALLEL - DGRAD+RS",
],
)
def test_linear_with_overlap_compile(
linear_parallel_mode, overlap_rs_dgrad, quantization, compile_mode
):
"""te.Linear comm+GEMM overlap (Userbuffers) under torch.compile,
checked numerically against the eager, non-overlap reference."""
if quantization is not None and linear_parallel_mode == "row":
pytest.skip(
"FP8 row-parallel UB forces differentiable fp8_output, unsupported under compile."
)
_run_layer_with_overlap(
te.Linear.__name__,
linear_parallel_mode,
overlap_rs_dgrad,
quantization is not None,
quantization,
use_compile=True,
compile_mode=compile_mode,
)


@pytest.mark.parametrize("use_cublasmp", (False, True))
@pytest.mark.parametrize(
"quantization",
Expand Down
2 changes: 1 addition & 1 deletion tests/pytorch/test_hybrid_quantization.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ def test_supports_only_rowwise_all_gather_nvfp4_columnwise(self):
``gather_along_first_dim`` cannot operate on a columnwise-only
NVFP4 hybrid sub-storage. ``HybridQuantizer.supports_only_rowwise_all_gather``
must return True in this case so ``_linear_forward_impl`` /
``_linear_backward`` preserve rowwise data (which NVFP4 can
``_linear_backward_impl`` preserve rowwise data (which NVFP4 can
dequantize) instead.
"""
hq = HybridQuantizer(
Expand Down
Loading
Loading