[Kernel][Inkling] Fuse shared-expert partial addition into the Lamport collective (#50697)

Signed-off-by: Canlin Guo <canlinguosdu@gmail.com>
This commit is contained in:
Canlin Guo
2026-08-04 18:41:35 +00:00
committed by GitHub
parent 8ae8337ffa
commit edbc4969a7
3 changed files with 48 additions and 6 deletions
+18 -5
View File
@@ -5,7 +5,7 @@
from __future__ import annotations
from collections.abc import Iterable
from typing import Any
from typing import Any, TypeAlias
import regex as re
import torch
@@ -57,6 +57,8 @@ from .ops.norm import add_rmsnorm, embed_rmsnorm
from .sconv_swa_attn import _ATTN, _MLP, InklingConvState, InklingSconvMetadata
from .short_conv import InklingShortConv
InklingDelta: TypeAlias = torch.Tensor | tuple[torch.Tensor, torch.Tensor]
def _layer_id(name: str) -> int | None:
m = re.search(r"\.layers\.(\d+)\.", name)
@@ -64,7 +66,7 @@ def _layer_id(name: str) -> int | None:
def _sconv_add_norm(
delta: torch.Tensor,
delta: InklingDelta,
hidden: torch.Tensor,
sconv: InklingShortConv,
norm: InklingRMSNorm | None,
@@ -84,6 +86,10 @@ def _sconv_add_norm(
off_s, ws = sconv.owner.stream_ranges[sconv.stream_idx]
norm_w = norm.weight if norm is not None else None
eps = norm.variance_epsilon if norm is not None else 0.0
if isinstance(delta, tuple):
delta, shared_delta = delta
else:
shared_delta = None
mm = get_lamport_rs_conv(hidden.shape[-1], sconv.kernel_size)
if mm is not None and mm.usable(delta.shape[0]) and m is not None:
@@ -103,9 +109,12 @@ def _sconv_add_norm(
off_s,
ws,
sconv.owner.block_size,
shared_tensor=shared_delta,
)
# Fallback: NCCL RS -> shard sconv -> AG -> fused add(+rmsnorm).
if shared_delta is not None:
delta.add_(shared_delta)
shard = tensor_model_parallel_reduce_scatter(delta, dim=-1)
shard = sconv(shard.contiguous(), positions)
full = tensor_model_parallel_all_gather(shard, dim=-1)
@@ -194,7 +203,7 @@ class InklingDecoderLayer(nn.Module):
self,
positions: torch.Tensor,
hidden_states: torch.Tensor,
pending: tuple[torch.Tensor | None, InklingShortConv] | None = None,
pending: tuple[InklingDelta, InklingShortConv] | None = None,
defer_mlp_add: bool = False,
attn_in: torch.Tensor | None = None,
log_scaling: torch.Tensor | None = None,
@@ -217,7 +226,11 @@ class InklingDecoderLayer(nn.Module):
mlp_in, hidden_states = _sconv_add_norm(
attn_output, hidden_states, self.attn_sconv, self.mlp_norm, positions
)
mlp_output = self.mlp(mlp_in)
mlp_output = (
self.mlp.forward_partials(mlp_in)
if isinstance(self.mlp, InklingMoE)
else self.mlp(mlp_in)
)
if defer_mlp_add:
# Caller folds mlp_output (pre-reduce, pre-sconv) into the next
# fused sconv+add+rmsnorm.
@@ -326,7 +339,7 @@ class InklingModel(nn.Module):
self.config.log_scaling_alpha,
)
pending: tuple[torch.Tensor | None, InklingShortConv] | None = None
pending: tuple[InklingDelta, InklingShortConv] | None = None
for layer in self.layers[self.start_layer : self.end_layer]:
hidden_states, pending = layer(
positions,
+5 -1
View File
@@ -501,7 +501,7 @@ class InklingMoE(nn.Module):
weights, ids = self.gate.select_experts(gating_output)
return weights[:, :topk].contiguous(), ids[:, :topk].contiguous()
def forward(self, x: torch.Tensor) -> torch.Tensor | None:
def forward_partials(self, x: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
router_logits = self.gate.compute_logits(x)
num_tokens = x.shape[0]
# One gate select per layer: the routed slice is stashed for the
@@ -527,6 +527,10 @@ class InklingMoE(nn.Module):
)
self._routed_sel = None
return out, sink_out
def forward(self, x: torch.Tensor) -> torch.Tensor:
out, sink_out = self.forward_partials(x)
return out.add_(sink_out)
# -- weight loading ----------------------------------------------------
+25
View File
@@ -88,15 +88,18 @@ def _wait_pairs(ptr, offsets, mask):
@triton.jit
def _publish_input_kernel(
stage_ptr,
shared_ptr,
peer_ptrs,
peer_offset_u32,
stride_stage_t,
stride_shared_t,
C: tl.constexpr,
CS: tl.constexpr,
CS_P2: tl.constexpr,
SPLITS: tl.constexpr,
RANK: tl.constexpr,
WORLD: tl.constexpr,
HAS_SHARED: tl.constexpr,
USE_PDL: tl.constexpr,
launch_pdl: tl.constexpr,
):
@@ -120,6 +123,13 @@ def _publish_input_kernel(
mask=elem_mask,
other=0.0,
)
if HAS_SHARED:
shared = tl.load(
shared_ptr + token * stride_shared_t + owner * CS + split * CSS + elem,
mask=elem_mask,
other=0.0,
)
values = (values.to(tl.float32) + shared.to(tl.float32)).to(tl.bfloat16)
packed = _pack_bf16_pairs(values)
base = tl.load(ptrs + owner).to(tl.pointer_type(tl.uint32))
dst = (token * WORLD + RANK) * (CS // 2) + split * (CSS // 2) + pair
@@ -595,6 +605,7 @@ class LamportRSConv:
off_s: int,
ws: int,
block_size: int,
shared_tensor: torch.Tensor | None = None,
) -> tuple[torch.Tensor | None, torch.Tensor]:
"""Return ``(normed | None, new_residual)``, both shaped ``[T, 6144]``."""
tokens, hidden_size = residual.shape
@@ -608,6 +619,13 @@ class LamportRSConv:
or input_tensor.stride(1) != 1
):
raise ValueError("input_tensor must be channel-contiguous bf16 [T, 6144]")
if shared_tensor is not None and (
shared_tensor.shape != input_tensor.shape
or shared_tensor.dtype != input_tensor.dtype
or shared_tensor.device != input_tensor.device
or shared_tensor.stride(1) != 1
):
raise ValueError("shared_tensor must match input_tensor")
shard_size = hidden_size // self.world_size
if conv_weight.shape != (shard_size, self.window_size):
raise ValueError(
@@ -648,15 +666,22 @@ class LamportRSConv:
gather_warps = 4 if tokens >= 256 else 8
_publish_input_kernel[phase_grid](
input_tensor,
shared_tensor if shared_tensor is not None else input_tensor,
self.input_peer_ptrs,
input_offset,
input_tensor.stride(0),
(
shared_tensor.stride(0)
if shared_tensor is not None
else input_tensor.stride(0)
),
C=hidden_size,
CS=shard_size,
CS_P2=phase_tile_p2,
SPLITS=phase_splits,
RANK=self.rank,
WORLD=self.world_size,
HAS_SHARED=shared_tensor is not None,
USE_PDL=self.use_pdl,
launch_pdl=self.use_pdl,
num_warps=phase_warps,