Repository navigation
[SM100] Mega MoE: order task-info slot reads with acquire loads instead of a fence - #145
Merged
zcnrex merged 2 commits intoNov 10, 2026
Merged
Conversation
…ence Root cause: deepseek-ai#441 put `fence.acq_rel.cta` (MEMBAR.ALL.CTA) before every task-info slot release in the epilogue. Besides the slot reads, the fence waits for all of the thread's outstanding memory operations, including the previous block's remote combine stores, which costs ~6 us per fp8xfp4 Mega MoE call at a 32-rank decode shape. Fix: copy the slot after the full-barrier wait with two `ld.acquire.cta.shared::cta.v4.u32` loads and drop the fence. The acquire orders every later operation of the thread, including the release arrive, after the slot reads, at the same scope as deepseek-ai#441. ptxas emits no MEMBAR or CCTL for it, and nothing waits on unrelated stores. Validation (32 ranks over 8 GB300 nodes, fp8xfp4, 416 experts, top-6, 108 tokens/rank, BLOCK_M 64, CUDA graph, interleaved rounds): slowest-rank median 89.6-89.8 -> 83.5-83.8 us/call, equal to dropping the fence; outputs bitwise identical to the fenced kernel and across 200 graph replays x 10 calls on every rank.
weireweire
force-pushed
the
sgl-megamoe-slot-acquire-load
branch
from
November 10, 2026 05:46
c64e54e to
beb552c
Compare
zcnrex
reviewed
Nov 10, 2026
| DG_STATIC_ASSERT(sizeof(task_info_t) == 2 * sizeof(uint4), "Invalid task info size"); | ||
| const auto slot = reinterpret_cast<const uint4*>(task_infos + sched_stage_idx); | ||
| auto dst = reinterpret_cast<uint4*>(&task_info); | ||
| dst[0] = ptx::ld_acquire_shared(slot); |
Collaborator
There was a problem hiding this comment.
Can we reuse the existing CUTLASS_DEVICE uint32_t ld_acquire_cta_shared(const uint32_t* ptr)
Author
There was a problem hiding this comment.
ld_acquire_cta_shared only read 4 bytes which need 8 reads and be slower, but we can override it, changed
zcnrex
approved these changes
Nov 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
deepseek-ai#441 fixed the task-info slot release ordering by adding
fence.acq_rel.ctatoMegaMoEScheduler::release_task_info(). The epilogue calls this once per block.In SASS the fence is
MEMBAR.ALL.CTA. It waits for all of the thread's outstanding memory operations, not only the slot reads. The epilogue thread has just issued the previous block's remote combine stores and counter updates, so every release waits for them.At a 32-rank decode shape this costs ~6 us per fp8xfp4 call, about 7% of the kernel. The release only has to come after the slot reads.
Change
get_next_task()copies the 32-byte slot with twold.acquire.cta.shared::cta.v4.u32loads, right after the full-barrier wait.release_task_info()drops the fence.MEMBARorCCTLfor it. The SASS keeps the sameMEMBAR/CCTL/SYNCS.ARRIVEcounts as the unfenced kernel; the slot copy becomes ordered scalarLDS.volatilewith a"memory"clobber, so the copy also stays below the full-barrier wait. This covers the wait side, which Fix Mega MoE task info slot release ordering deepseek-ai/DeepGEMM#441 does not touch.Benchmark
Setup:
fp8_fp4_mega_moeacross 32 ranks on 8 GB300 nodes in one NVLink domain.fence.acq_rel.cta, current main)ld.acquire.ctaslot loads)asm volatile("" ::: "memory"))ld.acquire.clusterslot loadsmbarrier.arrive.release.clusteron the release, no fenceCorrectness:
Scope note
.release.ctaorder the reads before the scheduler's next write.release.clusteron the arrive: +2.3 us per call over no fence.ld.acquire.cluster: +0.8 us, and it addsCCTL..release.clusterto the remote arrive is a one-line follow-up, at that price.Alternative considered
Compiler barriers alone (
asm volatile("" ::: "memory")) after the full wait and before the arrive:Serving
DeepSeek-V4.1-Flash, disaggregated serving on GB300:
Test plan
ld.acquire.cluster.tests/test_mega_moe.pyat 4 ranks for fp8xfp4 and bf16xbf16, with and without the change: