Rewrite scale assembly: no .item() calls, no Python loops, fully GPU
Apply to_blocked swizzle on entire padded buffer at once instead of per-expert loops. No .item()/.cpu() calls. Fully cudagraph-safe.
This commit is contained in:
@@ -189,10 +189,12 @@ class CuTeDSLMoERunner:
|
||||
"""Assemble 2D-side activation scales (cudagraph-safe, no CPU sync).
|
||||
|
||||
Phase 1: Scatter x_sf rows into 128-aligned positions in padded_x_sf.
|
||||
Phase 2: Per-expert, copy from padded_x_sf at the right offset,
|
||||
pad_and_swizzle, then concatenate.
|
||||
Phase 2: Apply Blackwell 32_4_4 scale swizzle to the entire padded buffer.
|
||||
|
||||
The output has sum(padded_rows_per_expert) rows (variable per expert).
|
||||
Fully GPU, no .item()/.cpu()/.tolist(), no per-expert Python loops.
|
||||
The padded_x_sf_buf is pre-allocated with 128-row alignment per expert
|
||||
and column padding to multiples of 4, so we can swizzle the whole
|
||||
tensor at once.
|
||||
"""
|
||||
num_experts = self.num_experts
|
||||
K_sf = x_sf.shape[1]
|
||||
@@ -213,35 +215,23 @@ class CuTeDSLMoERunner:
|
||||
dst_rows = padded_expert_offsets[expert_assign] + local_row
|
||||
padded_x_sf[dst_rows, :K_sf] = x_sf
|
||||
|
||||
# Phase 2: Per-expert swizzle
|
||||
swizzled_parts = []
|
||||
for e in range(num_experts):
|
||||
n_padded = padded_rows_per_expert[e].item()
|
||||
if n_padded == 0:
|
||||
continue
|
||||
start = padded_expert_offsets[e].item()
|
||||
buf = per_expert_bufs[e]
|
||||
# buf is only 128 rows; process in 128-row chunks
|
||||
offset = start
|
||||
remaining = n_padded
|
||||
while remaining > 0:
|
||||
chunk = min(remaining, 128)
|
||||
buf.zero_()
|
||||
buf[:chunk, :K_sf] = padded_x_sf[offset:offset + chunk]
|
||||
swizzled = pad_and_swizzle_single(buf)
|
||||
swizzled_parts.append(swizzled)
|
||||
offset += chunk
|
||||
remaining -= chunk
|
||||
# Phase 2: Apply swizzle to the entire padded buffer at once.
|
||||
# The buffer is pre-allocated at fixed size (cudagraph-compatible).
|
||||
# Active rows are determined by padded_expert_offsets[num_experts],
|
||||
# but during cudagraph capture the token budget is fixed, so total_padded_rows
|
||||
# is constant across capture and replay.
|
||||
rows = padded_x_sf.shape[0]
|
||||
cols = padded_x_sf.shape[1]
|
||||
row_blocks = rows // 128 # already 128-aligned
|
||||
col_blocks = cols // 4 # already 4-aligned
|
||||
|
||||
all_flat = torch.cat([p.view(torch.uint8) for p in swizzled_parts], dim=0)
|
||||
all_flat = all_flat.view(torch.float8_e4m3fn)
|
||||
# Reshape to 2D: (total_padded_rows, padded_cols)
|
||||
# padded_cols comes from the swizzle: ceil_div(K_sf, 4) * 4 * 4
|
||||
# (128 rows per row_block, 4 cols per col_block, 32 sub-rows * 16 sub-cols per block)
|
||||
# Simpler: total elements / total_padded_rows
|
||||
total_padded_rows = padded_expert_offsets[num_experts].item()
|
||||
padded_cols = all_flat.shape[0] // total_padded_rows if total_padded_rows > 0 else 0
|
||||
return all_flat.reshape(total_padded_rows, padded_cols)
|
||||
blocks = padded_x_sf.view(row_blocks, 128, col_blocks, 4).permute(0, 2, 1, 3)
|
||||
rearranged = blocks.reshape(-1, 4, 32, 4).transpose(1, 2).reshape(-1, 32, 16)
|
||||
swizzled = rearranged.flatten().view(torch.float8_e4m3fn)
|
||||
# The GEMM only reads total_padded_rows worth of scale data.
|
||||
# Return the full swizzled buffer; the GEMM uses expert_offsets to
|
||||
# determine how many rows each expert gets.
|
||||
return swizzled
|
||||
|
||||
def compute_activation_global_scales(self, hidden_states_sample, topk_weights, topk_ids):
|
||||
"""Compute activation global scales from a warmup forward pass.
|
||||
|
||||
Reference in New Issue
Block a user