Skip to content

Commit d334a3f

Browse files
committed
perf(pt/dpa4): triton fusion kernel
1 parent 8a49405 commit d334a3f

16 files changed

Lines changed: 3693 additions & 163 deletions

File tree

deepmd/pt/entrypoints/freeze_pt2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,7 @@ def freeze_sezm_to_pt2(
857857
exported = move_to_device_pass(exported, target_device)
858858

859859
out_path_str = str(out_path)
860-
compile_options = build_inductor_compile_options()
860+
compile_options = build_inductor_compile_options(inference=True)
861861
# Keep AOTInductor aligned with the eval compile path. ``triton.max_tiles=1``
862862
# keeps data-dependent edge axes on Triton's x grid, whose bound is large
863863
# enough for production-scale neighbor lists.

deepmd/pt/model/descriptor/sezm_nn/so2.py

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,14 +1173,23 @@ def __init__(
11731173
# ``DP_CUTE_INFER``. Each flag is read once at construction so it becomes a
11741174
# compile-time constant in the traced (``make_fx``) graph, and each only
11751175
# takes effect during inference. ``DP_TRITON_INFER`` replaces the dense
1176-
# ``bmm`` rotation with fused Triton kernels; ``DP_CUTE_INFER`` selects a
1177-
# fused CuTe value-path operator and is independent of the Triton flag. The
1178-
# CuTe value-path entry is bound at the end of construction (once every
1179-
# submodule exists) and stays ``None`` when the backend is unavailable or
1180-
# the block layout is unsupported.
1176+
# ``bmm`` rotation with fused Triton kernels; ``DP_CUTE_INFER`` selects
1177+
# the experimental CuTe value-path operator instead. Both flags claim
1178+
# the same ``so2_message`` value path, so enabling them together has no
1179+
# coherent meaning and is rejected at construction. The fused
1180+
# value-path entries are bound at the end of construction (once every
1181+
# submodule exists) and stay ``None`` when the backend is unavailable
1182+
# or the block layout is unsupported.
11811183
self.use_triton_infer = use_triton_infer()
11821184
self.use_cute_infer = use_cute_infer()
1185+
if self.use_triton_infer and self.use_cute_infer:
1186+
raise ValueError(
1187+
"DP_TRITON_INFER and DP_CUTE_INFER are mutually exclusive: "
1188+
"both select the fused SO(2) value-path backend. Enable "
1189+
"exactly one of them."
1190+
)
11831191
self._cute_value_path = None
1192+
self._triton_value_path = None
11841193

11851194
# === Step 1. Split deterministic seeds at the module top-level ===
11861195
seed_so2_stack = child_seed(seed, 0)
@@ -1570,7 +1579,23 @@ def __init__(
15701579
self._flash_atten_fn = flash_atten_aggregate
15711580
self._build_row_ptr_fn = build_row_ptr
15721581

1573-
# === Step 13. Optional fused CuTe SO(2) value-path operator ===
1582+
# === Step 13. Optional fused Triton SO(2) value-path operators ===
1583+
# Fuses rotate-to-local, the radial degree mixing, the gated mixing
1584+
# stack, and the focus competition of ``so2_message`` into the
1585+
# ``sezm_triton::so2_rotate_mix`` / ``so2_mixing_stack`` operators.
1586+
# The factory validates the block layout (``mmax == 1``, gated stack
1587+
# with an identity final layer, supported focus widths) and returns
1588+
# ``None`` otherwise, leaving the reference path in charge.
1589+
if self.use_triton_infer:
1590+
from .triton.so2_value_path import (
1591+
make_triton_value_path,
1592+
)
1593+
1594+
self._triton_value_path = make_triton_value_path(self)
1595+
1596+
# === Step 14. Optional fused CuTe SO(2) value-path operator ===
1597+
# Experimental alternative backend; mutually exclusive with the Triton
1598+
# flag (enforced above).
15741599
if self.use_cute_infer:
15751600
from .cute import (
15761601
make_cute_value_path,
@@ -1922,7 +1947,15 @@ def so2_message(
19221947
src, dst = edge_cache.src, edge_cache.dst
19231948
n_edge = src.numel()
19241949

1925-
if self._cute_value_path is not None and not self.training:
1950+
if self._triton_value_path is not None and not self.training:
1951+
# === Steps 1-5 (fused Triton operators). ``so2_rotate_mix`` folds
1952+
# the rotation and the radial degree mixing into one edge-parallel
1953+
# kernel writing the focus-major layout; ``so2_mixing_stack`` runs
1954+
# the whole gated stack with the competition weight fused into its
1955+
# final store, keeping the inter-layer activations off the traced
1956+
# graph. ===
1957+
x_local, rad_feat = self._triton_value_path(x, edge_cache, radial_feat)
1958+
elif self._cute_value_path is not None and not self.training:
19261959
# === Steps 1-5 (fused CuTe operator). The operator folds
19271960
# rotate_to_local, radial degree mixing, the multi-layer gated SO(2)
19281961
# stack, and the focus competition into the bucketed kernels; the

deepmd/pt/model/descriptor/sezm_nn/triton/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
descriptor; the package-level API only exposes availability.
77
"""
88

9+
from .force_assembly import (
10+
FORCE_ASSEMBLY_TRITON_AVAILABLE,
11+
)
912
from .radial_mix import (
1013
RADIAL_MIX_TRITON_AVAILABLE,
1114
)
@@ -15,6 +18,12 @@
1518
from .so2_rotation import (
1619
TRITON_ROTATION_AVAILABLE,
1720
)
21+
from .so2_value_path import (
22+
SO2_VALUE_PATH_TRITON_AVAILABLE,
23+
)
24+
from .wigner_monomials import (
25+
WIGNER_MONOMIALS_TRITON_AVAILABLE,
26+
)
1827

1928
# Every kernel module guards its ``@triton.jit`` definitions behind a ``triton``
2029
# import, so the module-level checks are equivalent. Expose a single
@@ -23,6 +32,9 @@
2332
TRITON_ROTATION_AVAILABLE
2433
and RADIAL_MIX_TRITON_AVAILABLE
2534
and SO2_BLOCK_GEMM_TRITON_AVAILABLE
35+
and SO2_VALUE_PATH_TRITON_AVAILABLE
36+
and WIGNER_MONOMIALS_TRITON_AVAILABLE
37+
and FORCE_ASSEMBLY_TRITON_AVAILABLE
2638
)
2739

2840
__all__ = [

0 commit comments

Comments
 (0)