Skip to content

Commit b5c48d7

Browse files
committed
fix(pt): address Neo CuTe review findings
1 parent 8d11cd0 commit b5c48d7

28 files changed

Lines changed: 240 additions & 210 deletions

deepmd/kernels/cute/neo/k1.py

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@ class _RunnerState:
482482

483483
_REGISTRY: dict[int, _RegistryEntry] = {}
484484
_NEXT_HANDLE = 1
485+
_REGISTRY_LOCK = threading.Lock()
485486
_PACKED_RUNNER_CACHE: dict[tuple[str, int | None, int], _RunnerState] = {}
486487
_PACKED_RUNNER_CACHE_LOCK = threading.Lock()
487488

@@ -523,27 +524,30 @@ def _compile_output_gate_backward(
523524
def register_cute_k1_block(block: Any, config: Any) -> int:
524525
"""Register a DeePMD block/config pair and return a stable integer handle."""
525526
global _NEXT_HANDLE
526-
handle = _NEXT_HANDLE
527-
_NEXT_HANDLE += 1
528527

529528
def remove_collected_entry(block_ref: weakref.ReferenceType[Any]) -> None:
530-
entry = _REGISTRY.get(handle)
531-
if entry is not None and entry._block_ref is block_ref:
532-
_REGISTRY.pop(handle, None)
533-
534-
_REGISTRY[handle] = _RegistryEntry(
535-
block=block,
536-
config=config,
537-
on_collect=remove_collected_entry,
538-
)
529+
with _REGISTRY_LOCK:
530+
entry = _REGISTRY.get(handle)
531+
if entry is not None and entry._block_ref is block_ref:
532+
_REGISTRY.pop(handle, None)
533+
534+
with _REGISTRY_LOCK:
535+
handle = _NEXT_HANDLE
536+
_NEXT_HANDLE += 1
537+
_REGISTRY[handle] = _RegistryEntry(
538+
block=block,
539+
config=config,
540+
on_collect=remove_collected_entry,
541+
)
539542
return handle
540543

541544

542545
def invalidate_cute_k1_state(block: Any) -> None:
543546
"""Release a block's registered CuTe state after its modules change."""
544547
state = getattr(block, "_deepmd_cute_k1_state", None)
545548
if isinstance(state, _RegisteredK1State):
546-
_REGISTRY.pop(state.handle, None)
549+
with _REGISTRY_LOCK:
550+
_REGISTRY.pop(state.handle, None)
547551
if hasattr(block, "_deepmd_cute_k1_state"):
548552
delattr(block, "_deepmd_cute_k1_state")
549553
if hasattr(block, "_deepmd_cute_gate_expand_contract"):
@@ -568,15 +572,16 @@ def _register_cute_k1_state(
568572
_validate_gate_expand_index(block)
569573
old_state = getattr(block, "_deepmd_cute_k1_state", None)
570574
if isinstance(old_state, _RegisteredK1State):
571-
old_entry = _REGISTRY.get(old_state.handle)
572-
if (
573-
old_state.device_index == device_index
574-
and old_state.config == config
575-
and old_entry is not None
576-
and old_entry.block is block
577-
):
578-
return old_state
579-
_REGISTRY.pop(old_state.handle, None)
575+
with _REGISTRY_LOCK:
576+
old_entry = _REGISTRY.get(old_state.handle)
577+
if (
578+
old_state.device_index == device_index
579+
and old_state.config == config
580+
and old_entry is not None
581+
and old_entry.block is block
582+
):
583+
return old_state
584+
_REGISTRY.pop(old_state.handle, None)
580585
if not _module_state_is_aligned(block):
581586
# Module parameters and buffers are frozen for this inference path, so
582587
# cache a failed static contract until explicit state invalidation.
@@ -1074,7 +1079,6 @@ def _final_manual_backward(runner: Any, grad_out: Tensor) -> tuple[Tensor, Tenso
10741079
grad_post_norm_in.squeeze(2).unsqueeze(2),
10751080
).squeeze(2)
10761081

1077-
grad_x_wide_down = None
10781082
if so2.message_node_grid_product is not None:
10791083
if runner.packed_message_grid:
10801084
message_grid_product = runner.message_grid_product
@@ -1099,14 +1103,13 @@ def _final_manual_backward(runner: Any, grad_out: Tensor) -> tuple[Tensor, Tenso
10991103
grad_post_mix,
11001104
)
11011105
grad_out_gate_flat.add_(grad_post_mix)
1102-
if grad_x_wide_down is None:
1103-
grad_x_wide_down = torch.zeros(
1104-
n_node,
1105-
16 * 64,
1106-
device=x_wide.device,
1107-
dtype=x_wide.dtype,
1108-
).view(n_node, 16, 64)
1109-
grad_x_wide_down.add_(grad_grid_context)
1106+
grad_x_wide_down = torch.zeros(
1107+
n_node,
1108+
16 * 64,
1109+
device=x_wide.device,
1110+
dtype=x_wide.dtype,
1111+
).view(n_node, 16, 64)
1112+
grad_x_wide_down.add_(grad_grid_context)
11101113
else:
11111114
grad_out_gate_flat = grad_post_mix
11121115
grad_x_wide_down = torch.zeros(
@@ -1354,7 +1357,8 @@ def _build_runner(
13541357
here and repair contiguous offset views without introducing a Dynamo graph
13551358
break above the op.
13561359
"""
1357-
entry = _REGISTRY[int(handle)]
1360+
with _REGISTRY_LOCK:
1361+
entry = _REGISTRY[int(handle)]
13581362
config = entry.config
13591363
if config.native_sm90_path:
13601364
from .sm90_k1.runner import NeoSm90K1Runner as Runner
@@ -1611,7 +1615,6 @@ def _runner_backward_manual(
16111615
runner.focus_alpha,
16121616
runner.dst_ptr_i32,
16131617
runner.rotate,
1614-
runner.attn_logits.detach().contiguous(),
16151618
runner.edge_gate,
16161619
so2.adamw_attn_z_bias_raw.detach().reshape(2).float().contiguous(),
16171620
runner.group_max,
@@ -1992,9 +1995,10 @@ def _maybe_run_prepared_cute_k1(
19921995
state = getattr(block, "_deepmd_cute_k1_state", None)
19931996
if not isinstance(state, _RegisteredK1State):
19941997
return None
1995-
entry = _REGISTRY.get(state.handle)
1996-
if entry is None or entry.block is not block:
1997-
return None
1998+
with _REGISTRY_LOCK:
1999+
entry = _REGISTRY.get(state.handle)
2000+
if entry is None or entry.block is not block:
2001+
return None
19982002
d_full = edge_cache.D_packed
19992003
dt_full = d_full
20002004
destinations_sorted = bool(getattr(edge_cache, "destinations_sorted", False))

deepmd/kernels/cute/neo/k1_kernels/cute_envelope_gated_softmax.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import cutlass
2323
import cutlass.cute as cute
24+
import cutlass.utils
2425
from cuda.bindings.driver import (
2526
CUstream,
2627
)

deepmd/kernels/cute/neo/k1_kernels/cute_neo_gate_linear_residual_backward_fused.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import cutlass
1414
import cutlass.cute as cute
15+
import cutlass.utils
1516
from cuda.bindings.driver import (
1617
CUstream,
1718
)

deepmd/kernels/cute/neo/k1_kernels/cute_neo_phase_a_radial_forward.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,11 +265,19 @@ def _combined_radial_weight(radial_hidden_proj, radial_degree_mixer):
265265
cache = getattr(radial_hidden_proj, "_deepmd_cute_neo_radial_combined", None)
266266
key = (
267267
hidden_weight.data_ptr(),
268-
mixer_weight.data_ptr(),
269268
hidden_weight._version,
270-
mixer_weight._version,
271269
hidden_weight.dtype,
272270
hidden_weight.device,
271+
tuple(hidden_weight.shape),
272+
tuple(hidden_weight.stride()),
273+
hidden_weight.storage_offset(),
274+
mixer_weight.data_ptr(),
275+
mixer_weight._version,
276+
mixer_weight.dtype,
277+
mixer_weight.device,
278+
tuple(mixer_weight.shape),
279+
tuple(mixer_weight.stride()),
280+
mixer_weight.storage_offset(),
273281
)
274282
if cache is not None and cache[0] == key:
275283
return cache[1]
@@ -308,19 +316,45 @@ def run_neo_phase_a_radial_forward_packed_direct(
308316
f"expected radial_feat_m0 shape {(edge_count, 4, 32)}, "
309317
f"got {tuple(radial_feat_m0.shape)}"
310318
)
319+
device = x_wide.device
320+
if device.type != "cuda":
321+
raise ValueError("packed Phase-A/radial forward requires CUDA tensors")
322+
if src.device != device or src.dtype not in (torch.int32, torch.int64):
323+
raise ValueError("src must be an int32 or int64 tensor on the input device")
324+
if src.data_ptr() % 16:
325+
raise ValueError("src must be 16-byte aligned")
326+
source_tensors = (
327+
("x_wide", x_wide),
328+
("D_full", D_full),
329+
("radial_feat_m0", radial_feat_m0),
330+
("radial_hidden_proj.weight", radial_hidden_proj.weight),
331+
("radial_degree_mixer.weight", radial_degree_mixer.weight),
332+
("radial_degree_mixer.channel_basis", radial_degree_mixer.channel_basis),
333+
)
334+
for name, tensor in source_tensors:
335+
if tensor.device != device or tensor.dtype != torch.float32:
336+
raise ValueError(f"{name} must be FP32 on {device}")
337+
if tensor.data_ptr() % 16:
338+
raise ValueError(f"{name} must be 16-byte aligned")
311339
if radial_hidden_proj.bias is not None:
312340
raise NotImplementedError("collapsed radial mixer expects no hidden bias")
341+
if tuple(radial_hidden_proj.weight.shape) != (32, 64):
342+
raise NotImplementedError("collapsed radial mixer expects a (32,64) projection")
313343
if radial_degree_mixer.mode != "degree_channel" or radial_degree_mixer.rank != 1:
314344
raise NotImplementedError(
315345
"collapsed radial mixer expects degree_channel rank=1"
316346
)
317347
if tuple(radial_degree_mixer.weight.shape) != (4 * 64, 25):
318348
raise NotImplementedError("collapsed radial mixer expects lmax=3,mmax=1,C=64")
349+
if tuple(radial_degree_mixer.channel_basis.shape) != (64,):
350+
raise NotImplementedError("collapsed radial mixer expects 64 channel weights")
319351

320352
combined_weight = _combined_radial_weight(
321353
radial_hidden_proj,
322354
radial_degree_mixer,
323355
)
356+
if not combined_weight.is_contiguous() or combined_weight.data_ptr() % 16:
357+
raise ValueError("combined radial weight must be contiguous and aligned")
324358
kernel = _compiled_neo_phase_a_radial_forward_packed_direct()
325359
out = torch.empty(
326360
edge_count,

deepmd/kernels/cute/neo/k1_kernels/cute_neo_phase_c_backward_layout.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ class NeoPhaseCBackwardLayoutParams:
7070
focus_alpha: cute.Tensor
7171
dst_ptr: cute.Tensor
7272
rotate_inv_rescale: cute.Tensor
73-
logits: cute.Tensor
7473
edge_gate: cute.Tensor
7574
z_bias_raw: cute.Tensor
7675
group_max: cute.Tensor
@@ -445,7 +444,6 @@ def neo_phase_c_backward_layout_jit(
445444
focus_alpha: cute.Tensor,
446445
dst_ptr: cute.Tensor,
447446
rotate_inv_rescale: cute.Tensor,
448-
logits: cute.Tensor,
449447
edge_gate: cute.Tensor,
450448
z_bias_raw: cute.Tensor,
451449
group_max: cute.Tensor,
@@ -473,7 +471,6 @@ def neo_phase_c_backward_layout_jit(
473471
focus_alpha=focus_alpha,
474472
dst_ptr=dst_ptr,
475473
rotate_inv_rescale=rotate_inv_rescale,
476-
logits=logits,
477474
edge_gate=edge_gate,
478475
z_bias_raw=z_bias_raw,
479476
group_max=group_max,
@@ -580,12 +577,6 @@ def compile_neo_phase_c_backward_layout(
580577
stride_order=(0,),
581578
**FAKE_TENSOR_KW,
582579
)
583-
fake_logits = make_fake_compact_tensor(
584-
cutlass.Float32,
585-
(edge_count, N_FOCUS),
586-
stride_order=(1, 0),
587-
**FAKE_TENSOR_KW,
588-
)
589580
fake_edge_gate = make_fake_compact_tensor(
590581
cutlass.Float32,
591582
(edge_count,),
@@ -680,7 +671,6 @@ def compile_neo_phase_c_backward_layout(
680671
fake_focus_alpha,
681672
fake_dst_ptr,
682673
fake_rotate,
683-
fake_logits,
684674
fake_edge_gate,
685675
fake_z_bias,
686676
fake_group_max,

deepmd/kernels/cute/neo/k1_kernels/cute_neo_phase_c_backward_layout_runner.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ def __call__(
175175
focus_alpha: torch.Tensor,
176176
dst_ptr: torch.Tensor,
177177
rotate_inv_rescale: torch.Tensor,
178-
logits: torch.Tensor,
179178
edge_gate: torch.Tensor,
180179
z_bias_raw: torch.Tensor,
181180
group_max: torch.Tensor,
@@ -225,7 +224,6 @@ def __call__(
225224
(DEGREE_COUNT,),
226225
device=device,
227226
)
228-
_require_tensor("logits", logits, (edge_count, N_FOCUS), device=device)
229227
_require_tensor("edge_gate", edge_gate, (edge_count,), device=device)
230228
_require_tensor("z_bias_raw", z_bias_raw, (N_FOCUS,), device=device)
231229
_require_tensor("group_max", group_max, (node_count, N_FOCUS), device=device)
@@ -296,7 +294,6 @@ def __call__(
296294
("focus_alpha", focus_alpha),
297295
("dst_ptr", dst_ptr),
298296
("rotate_inv_rescale", rotate_inv_rescale),
299-
("logits", logits),
300297
("edge_gate", edge_gate),
301298
("z_bias_raw", z_bias_raw),
302299
("group_max", group_max),
@@ -361,7 +358,6 @@ def __call__(
361358
focus_alpha,
362359
dst_ptr,
363360
rotate_inv_rescale,
364-
logits,
365361
edge_gate,
366362
z_bias_raw,
367363
group_max,

deepmd/kernels/cute/neo/k1_kernels/cute_neo_phase_c_onepass.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
import cutlass
3434
import cutlass.cute as cute
35+
import cutlass.utils
3536
from cuda.bindings.driver import (
3637
CUstream,
3738
)
@@ -58,6 +59,9 @@
5859
REDUCED_COUNT = 10
5960
FOCUS_COUNT = 2
6061
CHANNELS = 32
62+
63+
if PACKED_WIGNER_VALUES != 46 or PACKED_WIGNER_VALUES > 2 * CHANNELS:
64+
raise RuntimeError("one-pass Neo Phase-C requires the 46-value Wigner layout")
6165
HIDDEN = FOCUS_COUNT * CHANNELS
6266
PHASE_WIDTH = REDUCED_COUNT * HIDDEN
6367
OUTPUT_WIDTH = DEGREE_COUNT * HIDDEN

deepmd/kernels/cute/neo/k1_kernels/cute_neo_so2_gate_combined_fwd.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
M0_WIDTH = 4 * 32
4747
PAIR_WIDTH = 6 * 32
4848
FULL_WIDTH = M0_WIDTH + PAIR_WIDTH
49+
DEFAULT_STREAM = cuda.CUstream(cuda.CUstream_flags.CU_STREAM_DEFAULT)
4950

5051

5152
def _supports_combined_forward(compute_capability: tuple[int, int]) -> bool:
@@ -112,7 +113,7 @@ def __call__(
112113
mGate: cute.Tensor,
113114
mY: cute.Tensor,
114115
mOut: cute.Tensor,
115-
stream: cuda.CUstream = cuda.CUstream(cuda.CUstream_flags.CU_STREAM_DEFAULT),
116+
stream: cuda.CUstream = DEFAULT_STREAM,
116117
):
117118
sA_layout = cute.make_layout(
118119
(TILE_M, TILE_K, STAGES),

deepmd/kernels/cute/neo/k1_radial_phase_a_node.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
from .compile_cache import (
1818
device_aware_lru_cache,
1919
)
20+
from .k1_wigner_layout import (
21+
PACKED_VALUE_COUNT,
22+
)
2023

2124
if TYPE_CHECKING:
2225
from torch import (
@@ -29,7 +32,7 @@
2932
HIDDEN = 64
3033
FOCUS_COUNT = 2
3134
FOCUS_HIDDEN = 32
32-
PACKED_WIGNER_VALUES = 46
35+
PACKED_WIGNER_VALUES = PACKED_VALUE_COUNT
3336
RADIAL_WIDTH = 4 * FOCUS_HIDDEN
3437
COMPACT_WIDTH = 25
3538
PROJECTION_INPUT_WIDTH = COMPACT_WIDTH + FOCUS_COUNT
@@ -239,6 +242,8 @@ def run_neo_radial_phase_a_backward_node_tiled(
239242
The kernel recomputes Phase A, fuses the focus-source adjoint, uses
240243
four-lane warp reductions with a 68-float shared row pitch, and packs the
241244
27-column radial projection input for one strict-FP32 matrix call.
245+
``grad_out_focus`` is repacked in place as projection workspace and must
246+
not be reused after this function returns.
242247
"""
243248
import torch
244249

deepmd/kernels/cute/neo/k1_runner.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -614,9 +614,17 @@ def ensure_backward_workspace(
614614
radial_scratch = getattr(self, "structural_scratch", None)
615615
if radial_scratch is None:
616616
radial_scratch = next(
617-
cache.logits
618-
for cache in self.stack_caches
619-
if cache.logits is not None
617+
(
618+
cache.logits
619+
for cache in self.stack_caches
620+
if cache.logits is not None
621+
),
622+
None,
623+
)
624+
if radial_scratch is None:
625+
raise RuntimeError(
626+
"Neo K1 backward requires a reusable radial scratch buffer; "
627+
"no stack layer stored gate logits"
620628
)
621629
if self.phase_c_stack.device.type == "cuda":
622630
stream = self.torch.cuda.current_stream(self.phase_c_stack.device)
@@ -843,6 +851,7 @@ def _build_forward_graph(self) -> None:
843851
self.group_max,
844852
self.denom,
845853
)
854+
self.attn_logits = None
846855
x_wide_down = self.x_wide.detach()
847856
from .k1_kernels.cute_neo_phase_c_onepass import (
848857
run_neo_phase_c_onepass_output_gate,

0 commit comments

Comments
 (0)