MC2 v300验证算子 - #74
Conversation
Add MoE dispatch pipeline operators for v300 (PTO one-level-arch): - group_token_old: three-stage MoE token grouping (histogram + scatter + counting sort) - group_token_vec / group_token_vec_mt: vectorized token grouping and multi-thread variant - mega_moe: full A8W8 wave self-loop MoE migration (mega_moe_sim) - moe_dispatch / moe_combine: dispatch (v2) and combine (v2) operators - kernel tests + multi-thread tests for all operators
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fb4d109091
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| #endif | ||
|
|
||
| static uint32_t topkIndex[kTopKEleNum + 2 * 4096]; | ||
| uint32_t *topkIndexAligned = (uint32_t *)(((uint64_t)topkIndex & ~0xFFFu) + 0x1000); |
There was a problem hiding this comment.
Preserve upper pointer bits when aligning input
On a 64-bit CPU build, ~0xFFFu is promoted as 0x00000000fffff000, so this expression discards the upper 32 bits of topkIndex and genTopkIndex writes through an invalid low address. The same alignment expression appears in the single- and multi-thread vector tests, so host simulation commonly crashes before exercising any kernel; use a uintptr_t-sized mask or an aligned declaration.
Useful? React with 👍 / 👎.
| gm_fl gmFlag(reinterpret_cast<float*>(wAddr + blk * SPLIT_BLOCK_SIZE + SPLIT_BLOCK_DATA_SIZE)); | ||
| TSTORE(gmFlag, flagTile); |
There was a problem hiding this comment.
Restrict the dispatch flag store to its 32-byte field
When dispatching any token, tile_fl contains 32 floats (128 bytes), but the store starts at byte 480 of a 512-byte block. It consequently writes 96 bytes into the next token record, corrupting its payload, and the final record writes beyond the window allocation. The flag field is only 32 bytes (8 floats), so the stored tile or write method must be sized accordingly.
Useful? React with 👍 / 👎.
| uint64_t winDataSizeOffsetEp_ = (uint64_t)dataState_ * (totalWinSizeEp / 2UL); | ||
| uint64_t winStatusOffset_ = COMBINE_STATE_OFFSET + dataState_ * WIN_STATE_OFFSET; | ||
| uint8_t* epWindowGM_ = windowBuf + winDataSizeOffsetEp_; |
There was a problem hiding this comment.
Allocate a full window after selecting the second half
On the first invocation the zeroed status toggles dataState_ to 1, placing epWindowGM_ halfway into windowBuf, while subsequent epOffset addressing still spans all BS * (K + SharedExpertNum) * hAlignWinSize bytes. The included combine test allocates only exactly that many bytes, so dispatching its final half of tokens writes past windowBuf; either each state half must have the full calculated size or the buffer must be doubled.
Useful? React with 👍 / 👎.
| if (hasExpertScalesFlag_) { | ||
| float scaleVal = expertScalesGM_[expertScaleBeginIdx_ * axisK_]; | ||
| TMULS(accTile, accTile, scaleVal); |
There was a problem hiding this comment.
Apply each expert scale before accumulating
For inputs where expert scales differ by slot or token, this multiplies the already-summed expert outputs by the first processed token's first scale. The required result is sum(scale[token, expert] * value[token, expert]); a single post-accumulation multiply is only equivalent for the all-0.25 test fixture and silently produces incorrect MoE outputs for normal nonuniform routing weights.
Useful? React with 👍 / 👎.
| int32_t ip = (int32_t)p; | ||
| for (int32_t i = 0; i < ip; ++i) r *= 2.0; | ||
| return r; |
There was a problem hiding this comment.
Handle negative exponents in the MegaMoE golden decoder
The generated E4M3 weights deliberately use exponents 4 through 8, so ref_fp8_e4m3 calls this helper with negative values for exponents 4–6. Casting them to a negative ip makes the loop execute zero times and returns 1 instead of 2^ip, while the kernel decoder correctly multiplies by 0.5 repeatedly; therefore the newly added golden comparison disagrees with the kernel for most generated weights and cannot validate a correct result.
Useful? React with 👍 / 👎.
| float* combine = reinterpret_cast<float*>(g_mmWorkspace + combineOffset); | ||
| for (uint32_t i = 0; i < tilingData.bs * tilingData.h; ++i) { | ||
| combine[i] = 0.0f; |
There was a problem hiding this comment.
Synchronize MegaMoE initialization and compute phases
In the documented four-thread execution, every PE clears the entire shared combine buffer and then immediately starts computing its own token partition without a barrier. A slower PE can therefore zero entries another PE has already produced; the later full-buffer unpermute and shared token-count reset/increment are also performed concurrently. Partition initialization/output work to one PE or add cross-PE barriers so the default multi-thread run is deterministic.
Useful? React with 👍 / 👎.
| int ret = 0; | ||
| if (cntMatch != (int)kExpertNum) ret = 1; | ||
| else if (secMatch != (int)kExpertPerRank) ret = 2; | ||
| else if (idMatch != idTotal) ret = 3; | ||
| else if (boundMatch != (int)(kExpertPerRank + 1)) ret = 4; | ||
| else if (sortMatch != (int)kBS) ret = 5; |
There was a problem hiding this comment.
Validate the vector kernel's pod-info output
This test initializes and passes tokenSuperPodInfo, but its reference construction and final return checks cover only counts, grouped IDs, boundaries, and sorting. Consequently the vector kernel can return entirely incorrect destination-pod metadata while this validation still reports PASS; compute the expected pod bits as the old-variant test does and include them in ret.
Useful? React with 👍 / 👎.
|
|
||
| make TESTCASE=mega_moe_sim BS=256 H=128 HIDDEN=256 diss | ||
| make TESTCASE=mega_moe_sim BS=64 H=32 HIDDEN=64 diss No newline at end of file |
There was a problem hiding this comment.
Register the new kernels with the full compilation driver
The repository's benchmark/one-level-arch/compile_all.sh uses an explicit operator list and none of the newly added group-token, dispatch, combine, or MegaMoE directories are present in it. Thus the standard full-compilation workflow never invokes these new compile.all scripts, allowing their build and integration failures to go unnoticed; add the new operator paths to that driver.
Useful? React with 👍 / 👎.
| gm_fl gmFlag(reinterpret_cast<float*>(rankGM + SPLIT_BLOCK_DATA_SIZE - 24 * 4)); | ||
| TSTORE(gmFlag, flagTile); | ||
|
|
||
| // 2) Load expandX data and store to window data area | ||
| for (int t = 0; t < HTiles; t++) { | ||
| auto gin = ex_iter(tkIndex, t); | ||
| tile_h dataTile; | ||
| TLOAD(dataTile, gin); | ||
| // Store to window (data at block start, skip flag area) | ||
| gm_w gmDst(reinterpret_cast<ExpandXType*>(rankGM) + t * TileW); | ||
| TSTORE(gmDst, dataTile); |
There was a problem hiding this comment.
Preserve payload bytes when packing combine flags
For H * sizeof(ExpandXType) > 384 (for example H=256 with __half), the payload tiles are stored contiguously through byte 511, then this 128-byte flag tile is written over bytes 384–511 of the same record. The code also does not place data around each 32-byte gap when blockCntPerToken_ > 1, so common larger hidden dimensions are read back with their tail replaced by flag values; pack the payload in 480-byte chunks and write only each block's 32-byte flag.
Useful? React with 👍 / 👎.
| float a = static_cast<float>(output[i]); | ||
| float b = static_cast<float>(refOutput[i]); | ||
| float diff = a - b; | ||
| if (diff < 0.0f) diff = -diff; | ||
| if (diff > 0.01f) { ret = 1; break; } |
There was a problem hiding this comment.
Reject non-finite values in numeric comparisons
If either the kernel output or reference is NaN, both diff < 0.0f and diff > 0.01f are false, so this validation reports PASS for that element. The MegaMoE error loops use the same comparison pattern and likewise leave their maxima unchanged for NaNs; explicitly check std::isfinite before applying tolerances so numerical failures cannot pass validation.
Useful? React with 👍 / 👎.
…LinxISA/SuperScalarModel#348) gfrun R2=1 root cause is in the PTO port of the operator (not the functional model), three defects fixed in moe_dispatch_v2.hpp: 1. SplitToCore uint32 underflow: with single-thread aivId_=0 < moeUsedAivNum_, newAivId = 0 - moeUsedAivNum_ = 0xFFFFFFFF -> startStatusIndex_=0xFFFFFFFC > endStatusIndex_ -> GetCumSum loop zero iterations -> sendCountsOut never written (stale per-expert counts / zeros instead of prefix sum [8,16,24,32]). Clamp to 0. 2. TSTORE flag-area overrun: 32B flag area per 512B block is smaller than the minimum 128B TSTORE tile; tile_fl stores wrote [blk*512+480, blk*512+608), clobbering the next block's data area (triple [512,524) / scale [524,528)). Replace with scalar writes strictly inside the 32B area (TokenToExpert + ClearLocalWindowDataFlags). 3. Missing block packing: original Ascend C packs the token record (x + triple + scale) into 480B data areas of 512B blocks via Copy stride {1,1,16,15}. The PTO port wrote the record contiguously, so x ([0,512)) overlapped block 0's flag area [480,512) and the flag write corrupted the last 8 floats of x. Add in-place repack (TokenToExpert, reverse block order) and unpack (CopyInAndOut, forward order) matching srcTokenCopyParams {blockCnt, 480, align, 0}. Verified: compile OK; gfrun R2=0 (sendCountsOut=[8,16,24,32], expertTokenNumsOut=[8,8,8,8], expandX/Idx/Scales all match ref, 117,027 insts).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 92c2efbe10
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| bool maskValid = (bitIdx < (uint32_t)((slotCount + 31) / 32)) && | ||
| (gatherMaskTensor_[bitIdx] & (1u << bitPos)); | ||
| if (!maskValid) { | ||
| validExpertIds_[i] = -1; |
There was a problem hiding this comment.
Populate the expert-mask bitmap before filtering
When IsExpertMask is enabled and at least one slot is active, gatherMaskTensor_ is still entirely zero because the preceding mask loop only counts active entries. This test consequently marks every validExpertIds_ entry as -1, so no active expert is dispatched and the reported counts and expanded outputs are empty; set the corresponding bitmap bits from xActiveMask before applying this selection.
Useful? React with 👍 / 👎.
| // --- SendToMoeExpert (A:1092-1197) --- | ||
| // Unified token-iteration path (A5 style for single-thread) | ||
| auto SendToMoeExpert = [&]() { | ||
| uint32_t validTokenNum = isTokenMaskFlag_ ? (uint32_t)(activeMaskBsCnt_ * axisK_) : (uint32_t)expertIdsCnt_; |
There was a problem hiding this comment.
Filter token-mask entries instead of truncating the input
When IsTokenMask is enabled for a partially active batch, this treats the first activeMaskBsCnt_ * K slots as valid rather than selecting slots belonging to tokens whose mask is true. An inactive token before an active one is therefore dispatched while the later active token is omitted, and the subsequent full-array count also includes untouched inactive slots; iterate the original token range with the mask or compact both token indices and expert IDs.
Useful? React with 👍 / 👎.
| if constexpr (HasAddRmsNorm) { | ||
| armAvgFactor_ = 0.0f; // from tiling | ||
| epsilon_ = 0.0f; // from tiling | ||
| } |
There was a problem hiding this comment.
Initialize RMSNorm with a nonzero averaging factor
Whenever HasAddRmsNorm is instantiated as true, these hard-coded zeros make every squared term multiplied by armAvgFactor_ vanish and leave denom equal to zero. The later reciprocal square root is therefore infinite, producing infinite or NaN yOut and rstdOut for every token; obtain the configured epsilon and use the RMS factor (normally 1/H) instead of zero.
Useful? React with 👍 / 👎.
| for (uint32_t t = 0; t < kThreadsPerBlock; t++) { | ||
| sum += cntLocal[t * expertNum + globalExpert]; |
There was a problem hiding this comment.
Synchronize the old four-PE reductions
In the checked four-PE multi_thread/group_token_old driver, a PE begins summing every private histogram immediately after completing only its own histogram, so it can read another PE's cntLocal before that PE has initialized or finished it. The later per-PE scatter merge and PE-0 counting sort have the same producer/consumer ordering problem, making the advertised multi-thread result race-dependent; add cross-PE barriers before each reduction or merge phase.
Useful? React with 👍 / 👎.
| int32_t elasticInfoTensor_[64] = {0}; | ||
| if (hasElasticInfoFlag_) { | ||
| uint32_t elasticInfoSize = (ELASTIC_INFO_OFFSET + RANK_LIST_NUM * epWorldSizeOriginal_) * sizeof(int32_t); | ||
| for (uint32_t i = 0; i < elasticInfoSize / sizeof(int32_t); i++) { | ||
| elasticInfoTensor_[i] = elasticInfo[i]; |
There was a problem hiding this comment.
Size the elastic-info buffer for the configured world
When HasElasticInfo is enabled with EpWorldSize >= 31, elasticInfoSize exceeds this fixed 64-element array (4 + 2 * EpWorldSize entries), so the following copy writes past the stack buffer before any elastic routing is performed. Allocate the local storage from the compile-time world size or reject unsupported world sizes before copying.
Useful? React with 👍 / 👎.
| volatile uint32_t* finisher = reinterpret_cast<volatile uint32_t*>(0x10009000ULL); | ||
| if (tokOk && maxAbsErr < 1e-2 && maxRelErr < 1e-2) { | ||
| *finisher = 0x5555; |
There was a problem hiding this comment.
Guard the simulator finisher on CPU builds
When the MegaMoE target is built through its documented PLAT=cpu path and run with the common sim target, this unconditional write dereferences the simulator-only MMIO address 0x10009000 in a normal host process. Both passing and failing validations therefore terminate with a segmentation fault instead of returning their diagnostic code; restrict finisher writes to the Linx/gfsim build.
Useful? React with 👍 / 👎.
| if constexpr (QuantMode > UNQUANT) { | ||
| // quantInst_.QuantProcess — compiled out under UNQUANT |
There was a problem hiding this comment.
Reject or implement dispatch quantization modes
Whenever QuantMode is instantiated as STATIC_QUANT, a dynamic quantization mode, or MX quantization, this selected branch performs no payload write at all. The function still fills the record metadata and arrival flags afterward, so the receive path treats the record as ready and copies stale window bytes into expandXOut; either implement the advertised quantization path or reject non-UNQUANT instantiations.
Useful? React with 👍 / 👎.
| if constexpr (QuantMode > UNQUANT) { | ||
| // Quantized pack path — compiled out under UNQUANT | ||
| } else { |
There was a problem hiding this comment.
Reject or implement combine quantization modes
Whenever QuantMode is greater than UNQUANT, this dispatch branch writes neither the quantized payload nor its arrival flag, and the corresponding dequantization branch later in the function is also empty. The combine loop therefore skips tokens whose flags remain unset or accumulates no expert data, leaving the result missing; implement both pack and dequantization handling or make unsupported modes fail at compile time.
Useful? React with 👍 / 👎.
The previous fix (92c2efb) repacked window blocks with scalar loops while the token copy still used TLOAD/TSTORE tiles, so a 512B window block was touched by both a tile access and scalar accesses in the same program region. That violates A3 (spec 1.4b:252-258): a block holds at most one tile access and a tile access never shares a block with a scalar one. gfsim reports 3x a3_tile_violation (tile_access_shares_block) and the TLSU pipeline deadlocks at cycle 18010 (TLSU_AS.md:265 — software that mixes the two on one address without a barrier owns the consequence). Replace the tile copies with scalar loops in both directions: - TokenToExpert: token data GM -> window (was TLOAD+TSTORE) - CopyInAndOut: window -> expandXOut (was TLOAD+TSTORE) All window-slot traffic is now scalar (data copy, triple, scale, block pack/unpack, flags), so every window block only ever sees scalar accesses. gfsim a3_tile_violation count drops 3 -> 0 and the deadlock point moves forward from blocks=3330 to blocks=12487 (cycle 40186, thread 0 B199 STD flushed) — that residual stall is a separate TimingSim flush/replay issue tracked on the model side. Verified: compile OK; gfrun R2=0 (164,205 insts); gfsim violation=0.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0471150243
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| bool isInputTokenMaskFlag_ = false; | ||
| bool isInputExpertMaskFlag_ = false; |
There was a problem hiding this comment.
Honor the combine active-mask input
When a caller supplies xActiveMask for a partially active batch, both mask-mode flags remain hard-coded to false, so the mask-counting and index-gathering branches never run and the combine path processes all BS tokens and expert slots. This silently writes outputs for masked tokens and includes masked expert contributions; expose the intended token/expert mask mode and initialize these flags from it instead of disabling both modes.
Useful? React with 👍 / 👎.
| uint32_t moeExpertPerRankNum_ = MoeExpertNum; | ||
| bool isInputTokenMaskFlag_ = false; | ||
| bool isInputExpertMaskFlag_ = false; | ||
| bool hasSharedExpertX_ = false; |
There was a problem hiding this comment.
Add the supplied shared-expert residual
Whenever sharedExpertX is non-null, this hard-coded false flag prevents the later AddSharedExpertX branch from executing, so every output omits the advertised [BS, H] shared-expert contribution. Initialize the feature flag from the corresponding configuration or pointer so supplying this input changes the result.
Useful? React with 👍 / 👎.
| uint32_t sharedExpertNum_ = SharedExpertNum; | ||
| uint32_t sharedExpertRankNum_ = 0; | ||
| uint32_t moeExpertRankNum_ = epWorldSize_ - sharedExpertRankNum_; |
There was a problem hiding this comment.
Configure shared-expert ranks before dispatching
For a non-elastic instantiation with SharedExpertNum > 0, sharedExpertRankNum_ remains zero even though this is the only value used to divide shared experts among ranks. Consequently the execution condition at the dispatch call site never selects SendToSharedExpert, leaving every shared-expert window slot and arrival flag unwritten; initialize the rank count from configuration or reject this otherwise-advertised instantiation.
Useful? React with 👍 / 👎.
| uint32_t aivNum_ = 1; | ||
| uint32_t ubSize_ = 192U * 1024U; | ||
| uint32_t globalBS_ = BS; | ||
| bool hasElasticInfoFlag_ = false; |
There was a problem hiding this comment.
Apply elastic routing information during combine
When elasticInfo describes a scale-down/remapped EP topology, hasElasticInfoFlag_ is still fixed to false and elasticInfoGM_ is never read, leaving isScalingDownFlag_, the world size, and rank mappings at their compile-time values. The dispatch side can apply this remap, so combine then reads the pre-remap window slots and can omit or misattribute expert results; derive the elastic flag and topology from the supplied information.
Useful? React with 👍 / 👎.
| uint32_t scaleInBytes_ = 0; | ||
| uint32_t scaleOutBytes_ = 0; | ||
| uint32_t scalesCount_ = 0; |
There was a problem hiding this comment.
Preserve smooth-scale metadata in unquantized dispatch
When instantiated with QuantMode == UNQUANT and IsSmoothScaleExist == true, all scale sizes and counts remain zero, while the supplied scales input and dynamicScalesOut output are never accessed anywhere in the function. The record therefore contains only the token payload and ordinary expert scale, silently dropping the requested smooth-scale metadata; compute the scale layout and copy the scale values or reject this mode.
Useful? React with 👍 / 👎.
| // performanceTimeStart = GetSystemCycle() → 0 | ||
| uint64_t performanceTimeStart = 0; | ||
|
|
||
| // RecordRankCommDuration called inside polling loop (A:1863-1864) | ||
| // PTO: data already arrived → single iteration | ||
| if (isPerformanceFlag_ && performanceInfo != nullptr) { | ||
| int32_t* perfInfoTensor = performanceInfo; | ||
| int32_t perfFlags[MoeExpertNum * 4 > 0 ? MoeExpertNum * 4 : 1] = {0}; | ||
| int32_t duration = 0; // (0 - 0) / CYCLES_PER_US = 0 |
There was a problem hiding this comment.
Record real dispatch performance durations
Whenever IsPerformance is enabled, both the start cycle and computed duration are fixed to zero, so performanceInfo can never receive a positive communication duration regardless of how long dispatch takes. This makes the advertised performance output unusable for profiling experiments; use an available cycle source or reject performance mode rather than returning fabricated zero timings.
Useful? React with 👍 / 👎.
Adjacent i64 writes (e.g. tokRef[0]=0; tokRef[1]=0) get merged by the backend into a 16B tile store (BLK_TSTORE v2i64 BUILD_VECTOR), which the linxv5 ISel cannot select: "fatal error: error in backend: Cannot select: v2i64 = BUILD_VECTOR". Add volatile at the three trigger sites to keep scalar stores. Verified: BS256_H128_HD256 and BS64_H32_HD64 both build.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 096ce173f1
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| for (uint32_t i = 0; i < kExpertPerRank * kBS * kSuperPodNum; i++) { | ||
| if (tokenSuperPodInfo[i] == refPodInfo[i]) podMatch++; |
There was a problem hiding this comment.
Match pod metadata by token rather than scatter position
On the non-__linx/non-CPU hardware path, GroupToken_Vec_Impl assigns section positions using atomic increments, so tokens within a section need not appear in the scalar reference's sequential order. The grouped-ID check correctly treats each section as a set, but this positional comparison still compares pod metadata against the scalar ordering and can report FAIL for a correct hardware result. Validate each pod record together with its corresponding groupedTokenIds token instead.
Useful? React with 👍 / 👎.
| const int64_t* expertTokenNumsOut, | ||
| uint32_t bs, uint32_t h, uint32_t k, uint32_t moeExpertNum) { | ||
| const uint32_t slotCnt = bs * k; | ||
| int32_t counts[1024] = {0}; |
There was a problem hiding this comment.
Size dispatch reference counts from the expert count
When refDispatchCheck is used with moeExpertNum > 1024, which the templated dispatch kernel does not otherwise reject, the expert-ID counting loop writes beyond this fixed stack array; the subsequent cum[1024] has the same limitation. Larger-expert validation can therefore corrupt the stack or produce arbitrary results instead of checking the kernel, so allocate these buffers from moeExpertNum or enforce the limit explicitly.
Useful? React with 👍 / 👎.
…arModel#371 BS64 R2=9) Kernel side (per maintainer protocol in LinxISA/SuperScalarModel#371): - Wire kernel formal params (yOut/xIn/tokOut) — previously unused globals, ABI path for pointer-arg GM writeback now actually covered - Decouple 16 pseudo-core loop from thread count: every PE covers all cores idempotently (old tid*4+lc sharding under-covered when threads!=4) - Combine: direct per-token assignment instead of shared-region clear+"+="; removes cross-PE RMW races on the shared combine workspace (topK==1) - Stats export: per-slot register count + single idempotent store; removes clear+accumulate executed N times by N PEs (token counts were xN) Golden/host side: - exp2_approx: add missing negative-exponent branch (e4m3 exp field e<7 gave p<0; golden returned 1.0, mismatching kernel — 1t R2 failed too) - Golden tokRef counting and yRef combine made idempotent (multi-PE shared stack/GM: array-slot RMW "+=" executed by all 4 PEs -> x4; probe showed scalar loop=64 vs array-slot=256 at 4t) - BS16 build: volatile writes for topkIds/tilingData i64 zero fields — backend merges adjacent scalar zero stores into 16B tile store (BLK_TSTORE v2i64 BUILD_VECTOR, Cannot select); llvm-reduce minimal IR Verified with gfrun (SuperScalarModel main incl. PR #380): - BS64_H32_HD64: R2=0 @ threads 1 and 4 (was R2=9 @ 4t) - BS16_H128_HD256: R2=0 @ threads 1 and 4 (was fail-closed non-exec fetch) - BS256_H128_HD256: regression in progress
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c1465cd904
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| else if (idMatch != idTotal) ret = 3; | ||
| else if (boundMatch != (int)(kExpertPerRank + 1)) ret = 4; | ||
| else if (sortMatch != (int)kBS) ret = 5; |
There was a problem hiding this comment.
Validate pod metadata in the multi-thread vector test
The checked multi-thread test produces both perPePodInfo and tokenSuperPodInfo, but its reference data and return conditions validate only counts, grouped IDs, section boundaries, and sorting. Any regression in the per-PE pod merge can therefore return PASS while emitting incorrect destination-pod metadata; construct the expected pod bits for each grouped token and include that comparison in ret.
Useful? React with 👍 / 👎.
| MM_INLINE inline float fp8_e8m0_scale(uint8_t raw) | ||
| { | ||
| const int32_t e = static_cast<int32_t>(static_cast<int8_t>(raw)); | ||
| float s = 1.0f; |
There was a problem hiding this comment.
Apply the E8M0 exponent bias when decoding scales
For standards-compliant E8M0 scale tensors, the byte is an unsigned exponent with bias 127, but casting it to int8_t interprets it as a signed unbiased exponent. For example, the encoding 0x7f for scale 1 becomes 2^127, while 0x80 for scale 2 becomes 2^-128, so normal MegaMoE weights are decoded with catastrophic multipliers. The all-0x00 fixture and its reference decoder repeat this nonstandard interpretation and therefore mask the defect; decode using raw - 127 and handle the reserved encoding separately.
Useful? React with 👍 / 👎.
| expandIdx[i * 3] = 0; | ||
| expandIdx[i * 3 + 1] = i / kK; | ||
| expandIdx[i * 3 + 2] = i % kK; |
There was a problem hiding this comment.
Exercise non-identity unpermute mappings
This fixture assigns every expandIdx record its current token-major position, so the test never exercises the combine operator's unpermute behavior. Production dispatch output is expert-grouped and relies on these triplets to map each incoming row back to its original token and top-k slot; an implementation that ignores or misindexes expandIdx can therefore pass this validation. Populate a non-identity permutation and reorder expandX consistently before comparing with the token-major reference.
Useful? React with 👍 / 👎.
|
更新:mega_moe 用例侧多 PE 修复( kernel(
golden/host(
验证(gfrun = SuperScalarModel main 含 PR #380):BS16_H128_HD256 / BS64_H32_HD64 / BS256_H128_HD256 × threads 1/4 共 6 项全部 R2=0(BS64 原 R2=9、BS16 原编译崩溃/挂死)。 |
…omparison - moe_combine_full.hpp: full combine with 4/5 A5 infrastructures (512B block packing, pipeline sync via TMOV, batch flag check via TCMP, window state management), BS=8 H=128 K=4 aligned with A5 - reduce_fused_aligned.cpp: pure compute variant, BS=8 H=128 K=4 - reduce_fused_full.cpp: full comm+compute variant with pack/poll/clear gfrun PASS, gfsim: 5022 cycles (vs A5 cannsim 31506 cycles)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2395edf920
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| auto gf = flag_iter(slot, t); | ||
| TSTORE(gf, flagTile); |
There was a problem hiding this comment.
Allocate flags for every hidden tile
When H > TileW, t reaches 1 or more, but gm_flag and gm_pred are declared with only one TileW-wide tile per slot. Consequently flag_iter(slot, t) and the analogous predicate access alias subsequent slots and eventually run past the supplied buffers (for example, H=256, TileW=128); either allocate/index these tensors across H columns or store a single readiness tile without the t offset.
Useful? React with 👍 / 👎.
| for (int k = 0; k < K; k++) { | ||
| int slot = n * K + k; | ||
| if (predBuf[slot * TileW] < 0.5f) break; | ||
| } |
There was a problem hiding this comment.
Stop combining when a slot is not ready
When any slot's flag is unset, this break only exits a loop whose result is discarded; execution immediately proceeds to load and accumulate all K window rows. A missing or late expanded slot therefore contributes stale/uninitialized window data despite the readiness check, so the failed predicate must gate or defer the subsequent accumulation.
Useful? React with 👍 / 👎.
| void combine_phase(float *expertScales, DTypeIn *windowData, float *windowFlag, | ||
| float *predBuf, DTypeOut *out) | ||
| { | ||
| constexpr int kTiles = H / TileW; |
There was a problem hiding this comment.
For any permitted instantiation where H is not divisible by TileW (for example, H=192, TileW=128), integer division drops the final partial tile. Neither packing nor combining touches those hidden elements, leaving the output tail stale; process a remainder tile or add an H % TileW == 0 constraint.
Useful? React with 👍 / 👎.
|
|
||
| // 16 伪核全量循环: 与线程数解耦 (单线程/多线程均覆盖全部伪核, 结果幂等; | ||
| // 修正原 "tid*4+lc" 分片在线程数 != 4 时覆盖不足导致的 R2 失败) | ||
| const uint32_t perCore = tilingData.bs / kBlockAivNum; |
There was a problem hiding this comment.
Process remainder tokens after the 16-core split
When the configurable batch size is not divisible by kBlockAivNum (for example, BS=17), perCore is truncated and the following 16-core loop processes only 16 * floor(BS / 16) tokens. The remaining tokens are never sent through either GMM, retain stale outputs, and are omitted from expert counts; distribute the remainder or reject unsupported batch sizes explicitly.
Useful? React with 👍 / 👎.
| #endif | ||
|
|
||
| static uint32_t topkIndex[kTopKEleNum + 2 * 4096]; | ||
| uint32_t *topkIndexAligned = (uint32_t *)(((uint64_t)topkIndex & ~0xFFFu) + 0x1000); |
There was a problem hiding this comment.
Preserve the upper pointer bits when aligning the input
On a 64-bit host where the static array is mapped above 4 GiB, ~0xFFFu is a 32-bit mask that is zero-extended during the uint64_t bitwise operation, clearing the pointer's upper 32 bits and producing an invalid topkIndexAligned. The documented PLAT=cpu validation can therefore segfault before running the kernel; use a 64-bit mask such as ~uint64_t{0xFFF}. The same expression is repeated in the new group-token vector and multi-thread vector drivers.
Useful? React with 👍 / 👎.
| using gm_win = global_tensor<DTypeIn, RowMajor<NumExpanded, H>>; | ||
| using gm_flag = global_tensor<float, RowMajor<NumExpanded, TileW>>; | ||
| using gm_pred = global_tensor<float, RowMajor<NumExpanded, TileW>>; |
There was a problem hiding this comment.
Require the window to contain all BS-by-K slots
When NumExpanded < BS * K, which the template currently permits, these window, flag, and predicate tensors have only NumExpanded rows while the loop below unconditionally addresses slots 0 through BS * K - 1. Masked or compacted expansions therefore make the combine phase read and write beyond all three buffers; either size the window tensors by BS * K or enforce NumExpanded == BS * K.
Useful? React with 👍 / 👎.
| const uint32_t base = (round * kBlockAivNum + coreIdx) * kChunkElems; | ||
| tileShape t; | ||
| auto srcGT = xIter(0, base); | ||
| TLOAD(t, srcGT); |
There was a problem hiding this comment.
Pass a tile index to the MegaMoE fake-path iterator
When MEGA_MOE_SIM_FAKE is enabled, base is an element offset, but the second argument to global_iterator::operator() is a tile coordinate. After the first chunk, passing values such as 256 advances by 256 whole 256-element tiles rather than by 256 elements, so the fake path reads and writes far beyond xIn and yOut; use base / kChunkElems with a tensor covering the full input or offset the pointer and select tile zero.
Useful? React with 👍 / 👎.
| uint32_t curDstPod = topkIndex[j] >> 6; | ||
| dstPodLocal[curDstPod] = 1; |
There was a problem hiding this comment.
Bounds-check the scalar destination-pod index
On the __linx and CPU scalar path, any out-of-range or sentinel expert ID whose value is at least kExpertPerPod * kSuperPodNum produces curDstPod >= kSuperPodNum and writes past the two-element dstPodLocal array. The SIMT implementation and the scalar reference already guard this index, so the scalar fallback should apply the same curDstPod < superPodNum check rather than corrupting its stack.
Useful? React with 👍 / 👎.
- moe_dispatch_v2_aligned.hpp: full dispatch with 7/12 mechanisms aligned (512B stride, TMOV sync, check_flag, check_cumsum_flag, clear_flag) bf16 + TileW=128, windowData 512B stride matching A5 hCommuSize_ - moe_dispatch_v2_aligned.cpp: test main, BS=8 H=128 K=4 bf16 - moe_dispatch_v2.hpp: revert experimental tile window access in original (keep scalar A3-compliant version as-is) gfrun PASS (R2=0), gfsim: 11968 cycles (vs A5 cannsim 31177 cycles)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fc3f5cd519
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (hasExpertScalesFlag_) { | ||
| float scaleVal = expertScalesGM_[expertScaleBeginIdx_ * axisK_]; | ||
| TMULS(accTile, accTile, scaleVal); |
There was a problem hiding this comment.
Apply each slot's scale before accumulating
When top-k routing weights differ, this multiplies the completed sum by expertScales[beginIndex * K], producing scale[0] * sum(x_j) instead of sum(scale[j] * x_j) for every token on the single core. The earlier per-slot scaleVal is never applied, and the fixture's uniform 0.25f scales conceal the error; multiply each expert contribution by its corresponding scale before adding it.
Useful? React with 👍 / 👎.
| using gm_st = global_tensor<float, RowMajor<1, TileW>>; | ||
| auto gs = reinterpret_cast<gm_st*>(windowState + 4); | ||
| TSTORE(*gs, cumsumFlag); |
There was a problem hiding this comment.
Keep the cumsum flag inside the state allocation
With the default TileW=128, this stores 128 floats (512 bytes) beginning at windowState + 4, but the supplied aligned-dispatch driver allocates only uint32_t windowState[16] (64 bytes total). A tightly allocated caller therefore receives a 464-byte out-of-bounds write during every dispatch; use separate tile-sized flag storage or enlarge and document the state buffer.
Useful? React with 👍 / 👎.
| uint32_t sum = 0; | ||
| for (uint32_t t = 0; t < kThreadsPerBlock; t++) { | ||
| sum += cntLocal[t * expertNum + globalExpert]; | ||
| } |
There was a problem hiding this comment.
Synchronize vector PEs before shared reductions
In the checked four-PE multi_thread/group_token_vec execution, a PE starts reading all four cntLocal histograms as soon as its own histogram is ready, with no cross-PE barrier. It can therefore reduce uninitialized or incomplete peer data; the subsequent merge similarly consumes peer section buffers early, and PE 0 can sort before peers finish minLocalExpIds. Add barriers at each producer/consumer boundary and make the merge single-owner.
Useful? React with 👍 / 👎.
| const uint32_t scaleStride = (tilingData.h / 32U) * 2U * 2U * 2U; | ||
| w1[flat] = fp8_e4m3_to_f32(g_mmWeight1[flat]) * | ||
| fp8_e8m0_scale(g_mmWeightScales1[scaleIdx % scaleStride]); |
There was a problem hiding this comment.
Preserve per-expert MegaMoE scale indices
For the default H=128 and hiddenDim=256, each expert has 1024 weight-scale groups, but scaleStride is only 32 and this modulo maps every expert's first group back to index 0 while repeating just the first 32 entries across all weights. Thus distinct scales for expert 1 or later groups are ignored in both GMMs; the all-zero scale fixture and matching reference repeat the same addressing and mask the incorrect A8W8 result.
Useful? React with 👍 / 👎.
| // ---------- Init: window half-region selection ---------- | ||
| uint64_t winDataSizeOffsetEp_ = (uint64_t)dataState_ * (totalWinSizeEp / 2UL); | ||
| uint64_t winStatusOffset_ = COMBINE_STATE_OFFSET + dataState_ * WIN_STATE_OFFSET; | ||
| uint8_t* epWindowGM_ = windowBuf + winDataSizeOffsetEp_; |
There was a problem hiding this comment.
Allocate both combine window halves
On the first invocation the zero-initialized state selects dataState == 1, so this advances epWindowGM_ by half of totalWinSizeEp and then indexes a full BS * (K + SharedExpertNum) set of records from that point. The supplied combine driver allocates exactly totalWinSizeEp bytes, so its final half of record writes extends another half-buffer beyond windowBuf and into subsequent storage; either allocate two full record regions and offset by one full region, or treat the existing allocation as a single region without this half offset.
Useful? React with 👍 / 👎.
…omparison - mega_moe_full_sim.cpp: self-contained full GMM1→SwiGLU→GMM2→Combine pipeline using Cube TMATMUL (16 Cube tileops) + Vector tile ops. No mega_moe_sim.hpp include (avoids v2i64 BUILD_VECTOR crash). BS=8, H=128, HiddenDim=256, float, TileW=128. - mega_moe_gmm.hpp/cpp: independent translation unit for GMM tile functions (split to avoid compiler backend crash) - Makefile: add SIM_FAKE mode and mega_moe_full_sim target - mega_moe_sim.cpp: add SIM_FAKE static_assert relaxation for BS<16 gfrun PASS (R2=0), gfsim: 17516 cycles (Cube+Vector full pipeline) vs A5 cannsim: 18930 cycles (Vector-only, cannsim cannot simulate Cube)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4935b063f3
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| // --- SendToMoeExpert (A:1092-1197) --- | ||
| // Unified token-iteration path (A5 style for single-thread) | ||
| auto SendToMoeExpert = [&]() { | ||
| uint32_t validTokenNum = isTokenMaskFlag_ ? (uint32_t)(activeMaskBsCnt_ * axisK_) : (uint32_t)expertIdsCnt_; |
There was a problem hiding this comment.
Preserve sparse token-mask indices during dispatch
When IsTokenMask is enabled and inactive tokens are not a suffix, this truncates processing to the first activeMaskBsCnt_ * K slots rather than gathering the slots whose mask is true. For example, a mask [false, true, ...] dispatches token 0 and omits token 1; the later expert-count loop still counts all validExpertIds_, so it can also advertise records that were never written. Build and use an active-token index list instead of treating the active count as a prefix length.
Useful? React with 👍 / 👎.
| sendToMoeExpTokenCnt_ = 0; | ||
| for (int32_t i = 0; i < expertIdsCnt_; i++) { | ||
| if (xActiveMask[i]) sendToMoeExpTokenCnt_++; |
There was a problem hiding this comment.
Populate the expert-mask selection bitmap
Whenever IsExpertMask is true and at least one slot is active, this loop only increments sendToMoeExpTokenCnt_ and never sets the corresponding bits in gatherMaskTensor_. The selection at lines 503-515 therefore converts every validExpertIds_ entry to -1, producing zero expert counts and returning without any expanded output. Set a bitmap bit for each true mask entry (or compact the valid indices directly).
Useful? React with 👍 / 👎.
| gm_w gmDst(reinterpret_cast<ExpandXType*>(rankGM) + t * TileW); | ||
| TSTORE(gmDst, dataTile); |
There was a problem hiding this comment.
Pack combine records across every 512-byte block
When H * sizeof(ExpandXType) > 480 (for example, ExpandXType=float, H=128), these stores lay the payload contiguously across the first block instead of skipping its 32-byte flag area and initializing subsequent block flags. The payload then overwrites the readiness values at offsets 480-511, so the check at lines 523-529 treats every affected token as unready and leaves its output untouched. Apply the same 480-byte-data/32-byte-flag packing for every block that hAlignWinSize_ accounts for.
Useful? React with 👍 / 👎.
| uint32_t axisMaxBS_ = (epWorldSizeOriginal_ > 0) ? (globalBS_ / epWorldSizeOriginal_) : globalBS_; | ||
| uint64_t expertPerSizeOnWin_ = (uint64_t)axisMaxBS_ * hCommuSize_; |
There was a problem hiding this comment.
Reserve a full local batch per expert window partition
Whenever EpWorldSize > 1, each source rank still processes all BS input rows, but this limits every source/expert window partition to floor(BS / EpWorldSize) records. If more than that many local tokens route to one expert, dstTokenIdx advances into the next expert partition; routing any records to that next expert then overwrites the excess records, and the formatted expansion contains duplicated or missing tokens. Size each partition for the maximum locally dispatched count (at least BS for distinct top-k routes), or make BS represent and enforce the per-rank batch consistently.
Useful? React with 👍 / 👎.
| DType* windowData, float* windowFlag) | ||
| { | ||
| constexpr int slotCount = BS * K; | ||
| constexpr int hTiles = H / TileW; |
There was a problem hiding this comment.
Handle remainder columns in aligned dispatch
For any permitted instantiation where H is not divisible by TileW, integer division drops the final partial hidden tile. Both dispatch_pack here and dispatch_copy_out at line 169 use the truncated tile count, so the tail is never copied into the window or expandXOut and retains stale data. Process a masked remainder tile or add an H % TileW == 0 compile-time constraint.
Useful? React with 👍 / 👎.
| uint32_t moeExpertNumPerRank_ = (moeExpertRankNum_ > 0) ? (moeExpertNum_ / moeExpertRankNum_) : moeExpertNum_; | ||
| if (moeExpertNumPerRank_ == 0) moeExpertNumPerRank_ = moeExpertNum_; |
There was a problem hiding this comment.
Require an even expert partition across EP ranks
When MoeExpertNum is not divisible by EpWorldSize (for example, five experts over two ranks), this floor division assigns only four experts to the rank partitions. The remaining expert computes toRankId == EpWorldSize, while rscvStatusNum_ covers only the four partitioned experts; its status aliases another entry and sendCountsOut/expertTokenNumsOut never report it correctly. Either distribute the remainder experts explicitly or reject non-divisible configurations.
Useful? React with 👍 / 👎.
| using namespace pto; | ||
|
|
||
| using gm_x = global_tensor<DType, RowMajor<BS, H>>; | ||
| using gm_w = global_tensor<DType, RowMajor<slotCount, WindowStride>>; |
There was a problem hiding this comment.
Keep aligned window rows large enough for the hidden width
Whenever H > WindowStride, the tile stores for one slot continue past that slot's RowMajor<slotCount, WindowStride> row and overwrite the following slot's window data. The copy-out path uses the same undersized row shape, so overlapping records produce corrupted expanded tokens even when H is tile-aligned. Enforce WindowStride >= H or derive the stride from the hidden width and required padding.
Useful? React with 👍 / 👎.
…e_dispatch_v2 - Delete original moe_dispatch_v2.hpp (1287 lines, scalar window access) - Delete original moe_dispatch_v2.cpp - Rename moe_dispatch_v2_aligned.hpp → moe_dispatch_v2.hpp - Rename moe_dispatch_v2_aligned.cpp → moe_dispatch_v2.cpp - Update include guard, function name, compile.all - Remove test files (test_pack_copy.cpp, test_tile_copy.cpp) Only the aligned version (tile window access, TMOV sync, check_flag, 512B stride) is retained for A5 performance comparison.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1b6663533e
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if constexpr (HasAddRmsNorm) { | ||
| armAvgFactor_ = 0.0f; // from tiling | ||
| epsilon_ = 0.0f; // from tiling |
There was a problem hiding this comment.
Initialize RMSNorm scaling before normalization
Whenever HasAddRmsNorm is enabled, these constants remain zero, so the squared-value reduction is multiplied by zero and denom later becomes 0 + 0; 1 / sqrt(denom) therefore produces infinity and contaminates rstdOut and yOut with non-finite values for ordinary nonzero inputs. Initialize the averaging factor to 1/H and obtain a positive epsilon from the operator configuration.
Useful? React with 👍 / 👎.
| for (uint32_t t = 0; t < kThreadsPerBlock; t++) { | ||
| sum += cntLocal[t * expertNum + globalExpert]; |
There was a problem hiding this comment.
Synchronize PEs before reading peer histograms
In the documented four-PE multi_thread/group_token_old execution, each PE starts summing all four cntLocal rows immediately after finishing only its own row, with no cross-PE barrier. A faster PE can therefore read peer counters while they are still zero or partially populated and publish incorrect tokenPerExpertCnt values, making the experiment nondeterministically fail or report bad grouping statistics; synchronize after the local counting loop before this reduction.
Useful? React with 👍 / 👎.
| bool isInputTokenMaskFlag_ = false; | ||
| bool isInputExpertMaskFlag_ = false; |
There was a problem hiding this comment.
Honor the supplied active mask during combine
Whenever a caller supplies xActiveMask, both mask-mode flags remain hard-coded to false, so neither the one-dimensional token-mask count nor the two-dimensional expert-mask filtering below can execute. The combine consequently processes every token and top-k slot, allowing inactive expert contributions into the output; initialize the appropriate mask mode from the operator configuration instead of unconditionally disabling both paths.
Useful? React with 👍 / 👎.
| if constexpr (QuantMode > UNQUANT) { | ||
| // Quantized pack path — compiled out under UNQUANT | ||
| } else { |
There was a problem hiding this comment.
Implement or reject quantized combine modes
For every supported-looking instantiation with QuantMode > UNQUANT, this branch writes neither the token payload nor its readiness flag into the window. The later arrival check therefore skips the token and leaves its output stale; even with a preexisting flag, the matching dequantization branch is also empty and contributes zero. Implement the quantized pack/dequantize path or reject these modes at compile time.
Useful? React with 👍 / 👎.
| const float* w1Row = w1fp32 + e * h * 256 + nt * kVecW; | ||
| gmFlat gW(const_cast<float*>(w1Row)); | ||
| TLOAD(b, gW); | ||
| TMUL(acc, a, b); |
There was a problem hiding this comment.
Reduce across K in the vector GMM helper
When gmm_pipeline_for_token is called with its documented h=128 configuration, kTiles is one and this elementwise multiply is stored directly as the GMM1 output. Thus output lane n receives one product such as x[n] * w1[0][n] rather than sum_k(x[k] * w1[k][n]); the analogous GMM2 helper repeats the same pattern, so the exported pipeline cannot produce a matrix-multiplication result even at its default dimensions. Use a matrix operation or perform the required reduction over K.
Useful? React with 👍 / 👎.
| float diff = a - b; | ||
| if (diff < 0.0f) diff = -diff; | ||
| if (diff > 0.01f) { ret = 1; break; } |
There was a problem hiding this comment.
Reject non-finite combine outputs in the test
If the combine kernel produces NaN, then diff is also NaN and the comparison diff > 0.01f evaluates false, so an output containing non-finite values can still make this validation return success. This is especially relevant to the newly exposed normalization and quantized paths, where invalid arithmetic can generate non-finite results; explicitly fail when either actual or reference output is not finite.
Useful? React with 👍 / 👎.
| std::memcpy(&act_raw, &expandXOut[q * kH + j], 2); | ||
| uint16_t diff = exp_raw ^ act_raw; | ||
| if (diff != 0 && diff != 1) return 6; |
There was a problem hiding this comment.
Require exact bits for dispatch copies
The dispatch path only copies BF16 payloads and performs no arithmetic conversion, but this condition accepts an XOR difference of one. Consequently a regression that flips the least-significant bit of every copied BF16 element still passes the validation even though expandXOut is not a faithful dispatch of x; require act_raw == exp_raw for this copy test.
Useful? React with 👍 / 👎.
…mbine_v2 - Delete original moe_combine_v2.hpp (892 lines, scalar window access) - Rename moe_combine_full.hpp → moe_combine_v2.hpp (aligned: tile ops, TMOV sync, batch flag check, window state management) - Update include guard and function name: moe_combine_full → moe_combine_v2 - Rewrite test main (moe_combine_v2.cpp) to include new path - Delete reduce_fused_aligned.cpp and reduce_fused_full.cpp (test variants) - Fix mega_moe_full_sim.cpp constant naming conflict with Makefile -D macros gfrun PASS (R2=0, 977 blocks), gfsim: 5088 cycles (vs original 66942, -92%)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7b1b9bd8bb
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| mergeGroupTokenResults( | ||
| perPegroupedIds, perPeSectionCnt, perPePodInfo, | ||
| groupedTokenIds, tokenSuperPodInfo, expertSectionTokenCnt, | ||
| kExpertPerRank, kSuperPodNum); |
There was a problem hiding this comment.
Synchronize before merging per-PE scatter buffers
In the checked four-PE driver, each PE enters mergeGroupTokenResults immediately after producing only its own perPeSectionCnt, perPegroupedIds, and perPePodInfo, with no cross-PE barrier. A faster PE can therefore merge zero or incomplete peer sections and publish truncated grouping results; the merge should run only after all scatter producers finish, preferably on a single PE.
Useful? React with 👍 / 👎.
| TLOAD(y3, gY3); | ||
| TMULS(y3, y3, weight); | ||
| gmV gY(y + token * kMoeH); | ||
| TSTORE(gY, y3); |
There was a problem hiding this comment.
Compute every output column in the full simulation
With the fixed H=128 configuration, GMM2 computes only one 16-column tile in y3Padded; loading 128 contiguous values from that 16-by-16 buffer then writes flattened matrix rows as the token's 128 hidden values. Only the first 16 values belong to row 0, while columns 16–127 were never computed, so the mega_moe_full_sim target cannot represent the claimed full pipeline despite returning success.
Useful? React with 👍 / 👎.
| // gfsim 判读通道: test-finisher (0x10009000, 低 16 位 0x5555 = PASS) | ||
| volatile uint32_t* finisher = reinterpret_cast<volatile uint32_t*>(0x10009000ULL); | ||
| if (tokOk && maxAbsErr < 1e-2 && maxRelErr < 1e-2) { | ||
| *finisher = 0x5555; |
There was a problem hiding this comment.
Avoid device-MMIO writes during CPU simulation
In the PLAT=cpu path explicitly supported by this test's Makefile, the common sim rule executes this ELF directly as a host process, where 0x10009000 is not a mapped gfsim finisher device. A successful validation therefore reaches this store and segfaults instead of returning zero; guard finisher writes to the simulator/bare-metal environment.
Useful? React with 👍 / 👎.
| // ---- 完整参考 golden 对比 ---- | ||
| double* yRef = g_goldenY; | ||
| int64* tokRef = g_goldenTok; | ||
| compute_golden(yRef, tokRef); |
There was a problem hiding this comment.
Compare fake-mode output against the sigmoid reference
Whenever SIM_FAKE=1, the selected kernel path computes sigmoid(x), but the test still calls the full quantized MoE compute_golden routine here. Correct fake-path output will consequently be compared with an unrelated GMM/SwiGLU result and reported as a failure; select a sigmoid reference under MEGA_MOE_SIM_FAKE.
Useful? React with 👍 / 👎.
| for (uint32 i = 0; i < kTotalElems; ++i) { | ||
| const double err = (double)y[i] - yRef[i]; | ||
| const double absErr = err < 0 ? -err : err; | ||
| if (absErr > maxAbsErr) maxAbsErr = absErr; |
There was a problem hiding this comment.
Reject non-finite MegaMoE results
If either the kernel or reference produces NaN, absErr is also NaN and this comparison is false, leaving maxAbsErr at its previous value; the relative-error loop behaves the same way. Thus non-finite outputs can satisfy both final tolerances and report PASS, so explicitly fail on non-finite actual, reference, or error values.
Useful? React with 👍 / 👎.
| uint32_t cur = topkIndex[i * topk + j] % expertPerRank; | ||
| if (cur < minLocal) minLocal = cur; | ||
| } | ||
| blkv_get_tile_ptr(dst)[i] = minLocal; |
There was a problem hiding this comment.
Size the FloorFunc tile for the full batch
On the real-hardware path, the default batchSize is 512, but tile_shape_out is TileU32, whose 16-by-16 storage contains only 256 elements. The loop writes blkv_get_tile_ptr(dst)[i] through index 511, so the second half of the default batch runs beyond the output tile before the counting sort consumes it; use a tile covering all 512 entries or process the batch in multiple tiles.
Useful? React with 👍 / 👎.
| float frac = 1.0f + static_cast<float>(m) / 8.0f; | ||
| float exp2 = 1.0f; | ||
| int32_t bias = static_cast<int32_t>(e) - 7; | ||
| if (bias >= 0) { | ||
| for (int32_t i = 0; i < bias; ++i) exp2 *= 2.0f; | ||
| } else { | ||
| for (int32_t i = 0; i < -bias; ++i) exp2 *= 0.5f; | ||
| } | ||
| val = frac * exp2; |
There was a problem hiding this comment.
Preserve the reserved E4M3FN NaN encoding
For E4M3FN weights encoded as 0x7f or 0xff, exponent 15 and mantissa 7 represent NaN, but this generic normalized-number calculation decodes them as finite ±480. A standards-compliant tensor containing the reserved value will therefore silently inject a large finite weight rather than propagating invalid data; handle the reserved encoding before the normal exponent path.
Useful? React with 👍 / 👎.
| #!/bin/bash | ||
| : "${COMPILER_DIR:?Set COMPILER_DIR to the in-repo Linx compiler bin directory}" | ||
|
|
||
| make TESTCASE=group_token_old COMPILER_DIR=$COMPILER_DIR No newline at end of file |
There was a problem hiding this comment.
Register the new suites in the full compilation driver
The repository-wide benchmark/one-level-arch/compile_all.sh uses a hard-coded list of suite directories, and a repo-wide check shows that none of the newly added group_token_old, group_token_vec, mega_moe, moe_combine, moe_dispatch, or multi-thread group-token compile.all scripts are invoked. Consequently the advertised full compilation skips every test introduced by this commit, allowing these kernels to regress or stop compiling unnoticed; add the new suite paths to the top-level driver.
Useful? React with 👍 / 👎.
… pipeline in vec variant group_token_old / group_token_vec_mt multi-thread correctness fixes: - Add mtBarrier (volatile per-PE phase flags + compiler barrier, same convention as matmul RES_CHECK leader_ready) between phases: original code had cross-PE data hand-offs (Phase1 reduce, merge, Phase3b sort) with no synchronization at all. - Run mergeGroupTokenResults and the verification/reference compare on PE0 only: previously all 4 PEs executed the merge concurrently on the same output arrays (race + 4x duplicate work). - group_token_vec_mt.hpp: replace the pseudo-tile scheme with disjoint per-PE 4x16 tiles (PE tid owns rows [4*tid, 4*tid+3], trowsum/tadd convention). Phase 3a is now a real tile pipeline TLOAD -> TREMS -> TROWMIN -> TSTORE. Drop Phase 2 TLOAD whose data was never consumed by tile ops (scalar tile-register reads hit a linxv5 backend 'Cannot select: extract_vector_elt' crash, and the load was duplicated 4x across PEs). - Gate banner printf by tid==0; drop redundant zero-init of .bss arrays. Verified: both tests compile and pass gfrun 4-PE (multiThreadNum=4), R2=21b90 (old) / R2=29ef0 (vec).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9e57da233f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
|
||
| using gm_x = global_tensor<DTypeIn, RowMajor<NumExpanded, H>>; | ||
| using gm_win = global_tensor<DTypeIn, RowMajor<NumExpanded, H>>; | ||
| using gm_flag = global_tensor<float, RowMajor<NumExpanded, TileW>>; |
There was a problem hiding this comment.
Size flag storage for every hidden tile
When H > TileW, flag_iter(slot, t) advances by a full TileW tile even though each gm_flag row contains only TileW elements. The second hidden tile therefore aliases the next slot's flag row, and the final slot writes beyond windowFlag; gm_pred has the same layout and corruption in the combine phase. Allocate a flag/predicate tile per hidden tile or store one readiness flag per slot.
Useful? React with 👍 / 👎.
| void pack_phase(DTypeIn *expandX, std::int32_t *expandIdx, | ||
| DTypeIn *windowData, float *windowFlag) | ||
| { | ||
| constexpr int kTiles = H / TileW; |
There was a problem hiding this comment.
Handle the final partial hidden tile
For any allowed instantiation where H % TileW != 0, this floor division omits the final partial tile. Both packing and combining use the truncated count, so those hidden elements are never copied or written to out; process a masked remainder or enforce divisibility with a compile-time constraint.
Useful? React with 👍 / 👎.
| for (int k = 0; k < K; k++) { | ||
| int slot = n * K + k; | ||
| if (predBuf[slot * TileW] < 0.5f) break; | ||
| } |
There was a problem hiding this comment.
Stop combining when a contribution is not ready
When any slot's flag is absent or stale, this break exits only the short predicate-inspection loop; execution immediately initializes acc, reads every window slot, and stores an output anyway. The readiness result therefore has no effect, allowing incomplete or stale expert data into the combined token; skip or wait on the tile when the check fails.
Useful? React with 👍 / 👎.
| #endif | ||
|
|
||
| static uint32_t topkIndex[kTopKEleNum + 2 * 4096]; | ||
| uint32_t *topkIndexAligned = (uint32_t *)(((uint64_t)topkIndex & ~0xFFFu) + 0x1000); |
There was a problem hiding this comment.
Preserve upper pointer bits when aligning the test buffer
On a 64-bit CPU/PIE build, ~0xFFFu is a 32-bit mask that is zero-extended before the uint64_t AND, discarding the upper 32 bits of topkIndex. The resulting topkIndexAligned usually points into unmapped low memory, so the explicitly supported PLAT=cpu test crashes during input generation; use a uintptr_t-width mask. The same expression is repeated in the single-thread old and multi-thread vector drivers.
Useful? React with 👍 / 👎.
| int ret = 0; | ||
| if (cntMatch != (int)kExpertNum) ret = 1; | ||
| else if (secMatch != (int)kExpertPerRank) ret = 2; | ||
| else if (idMatch != idTotal) ret = 3; | ||
| else if (boundMatch != (int)(kExpertPerRank + 1)) ret = 4; |
There was a problem hiding this comment.
Verify the vector pod-info output
The vector kernel produces tokenSuperPodInfo, but this test computes no pod-info reference and none of the final pass conditions inspect that buffer. A regression that leaves all pod flags zero or associates them with the wrong grouped token therefore still reports PASS even though one of the operator's documented outputs is corrupt; validate it alongside the grouped IDs as the old-variant test does.
Useful? React with 👍 / 👎.
| q++; | ||
| } | ||
| } | ||
| return (q == slotCnt) ? 0 : 7; |
There was a problem hiding this comment.
Verify dispatched expert scales
Although the operator emits expandScalesOut and the test initializes nonzero expertScales, the success path never compares the emitted scales with the source slot ordering. A regression that omits the copy, writes zero scales, or associates a scale with the wrong expanded token therefore passes while causing the downstream combine to weight routed tokens incorrectly; check each scale while validating expandIdxOut.
Useful? React with 👍 / 👎.
…ceil-sharding for BS<16 moe_combine_v2: move kernels/deepseek/moe → kernels/moe_combine (dispatch-style layout: combine_pack/check_flag/clear_flag/combine_reduce with Phase banners and #N A5-alignment annotations); update test include path. mega_moe_sim: ceil-based 16-AIV pseudo-core token sharding with tail-core guard (bs<16: 1 token per core, idle tail cores; bs%16==0 identical to source), relax static_assert to kBS>0, SIM_FAKE round ceil + bounds check. Standard sim spec BS=16 H=32 HIDDEN=64 (gfsim passed, Total Cycles=2190707), aligned with A5 cann-samples mega_moe_story for v300/A5 perf comparison. Verified: moe_combine_v2 & mega_moe_sim BS16 compile OK, gfrun R2=0.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 982db09433
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| for (uint32_t t = 0; t < kThreadsPerBlock; t++) { | ||
| sum += cntLocal[t * expertNum + globalExpert]; | ||
| } |
There was a problem hiding this comment.
Synchronize histograms before reducing them
In the four-PE multi_thread/group_token_vec execution, each PE begins summing every PE's cntLocal row after completing only its own histogram. A faster PE can therefore read zero or partially populated peer rows and publish incorrect expert counts; the mtBarrier(1) in runGroupTokenVecMT occurs only after this reduction has already finished, so a barrier is needed between histogram construction and this loop.
Useful? React with 👍 / 👎.
| for (uint32_t i = 0; i < batchSize; i++) { | ||
| counts[minLocalExpIds[i]]++; | ||
| } |
There was a problem hiding this comment.
Synchronize row minima before counting-sort reads
In the four-PE vector driver, PE 0 enters this counting loop immediately after storing only its own subset of minLocalExpIds, while the other PEs may still be executing their tile stores. This can index counts with stale minima and produce incorrect section boundaries and sorted IDs; the existing mtBarrier(3) runs after sortKernel_mt_tile returns and is therefore too late to protect these reads.
Useful? React with 👍 / 👎.
| static float windowFlag[kSlotCount * kTileW] __attribute__((aligned(4096))) = {}; | ||
| static float predBuf[kSlotCount * kTileW] __attribute__((aligned(4096))) = {}; | ||
| static int32_t windowTriple[kSlotCount * 3] __attribute__((aligned(4096))) = {}; | ||
| static uint32_t windowState[16] __attribute__((aligned(4096))) = {}; |
There was a problem hiding this comment.
Reserve the full cumsum flag tile in windowState
With this test's default TileW=128, moe_dispatch_v2.hpp performs a TSTORE of 128 floats beginning at windowState + 4, which requires at least 132 uint32_t elements. This 16-element allocation therefore receives a 512-byte write starting 16 bytes into a 64-byte object, causing an out-of-bounds write and possible corruption of adjacent test buffers.
Useful? React with 👍 / 👎.
| moe_combine_v2<__bf16, __bf16, BS, H, K, NUM_EXPANDED, TILE_W>( | ||
| expand_x, expert_scales, expand_idx, window_data, window_flag, | ||
| window_state, pred_buf, out_buf); | ||
| return 0; |
There was a problem hiding this comment.
Validate combine outputs before returning success
This newly added combine test invokes the kernel and then unconditionally returns success without inspecting out_buf, flags, or state. Consequently failures such as skipped reductions, incorrect weighting, or a completely unwritten output all pass the suite, so the test needs a reference computation and explicit output checks before returning zero.
Useful? React with 👍 / 👎.
| for (uint32_t blk = 0; blk < kBS / kTileM; ++blk) { | ||
| auto src = gIter(blk, 0); | ||
| auto dst = oIter(blk, 0); |
There was a problem hiding this comment.
Advance each PE tile by the full 16-row block
In the four-PE vector sort, global_iterator advances i by the tile's four rows, so incrementing blk by one does not move to the next 16-row block as intended. Across all 32 iterations the four PEs repeatedly overlap rows and cover only rows 0–139, leaving minLocalExpIds[140..511] unwritten before the counting sort; stride the iterator by kThreadsPerBlock blocks or construct each source from blk * kTileM + tid * 4.
Useful? React with 👍 / 👎.
| uint32_t curDstPod = topkIndex[j] >> 6; | ||
| dstPodLocal[curDstPod] = 1; |
There was a problem hiding this comment.
Bounds-check pod IDs before indexing scratch storage
On the scalar simulator/CPU path, any out-of-range expert ID makes curDstPod exceed the two-element dstPodLocal array; for example expert 128 writes index 2, while a UINT32_MAX sentinel writes far outside the stack object. Phase 1 explicitly tolerates such IDs by ignoring experts outside expertNum, and the SIMT implementation checks curDstPod < superPodNum, so the scalar fallback should apply the same bound before this write.
Useful? React with 👍 / 👎.
| auto srcGT = xIter(0, base); | ||
| TLOAD(t, srcGT); | ||
| TMULS(t, t, -1.0f); | ||
| TEXP(t, t); | ||
| TADDS(t, t, 1.0f); | ||
| TRECIP(t, t); | ||
| auto dstGT = yIter(0, base); |
There was a problem hiding this comment.
Pass tile indices rather than element offsets in fake mode
Whenever SIM_FAKE=1 processes more than one 256-element chunk, base is already an element offset, but global_iterator::operator() multiplies its column argument by the tile width again. Thus the second logical chunk (base=256) loads and stores at element 65,536 rather than 256—already beyond the default 32,768-element tensors—and subsequent chunks corrupt still more distant memory; pass base / kChunkElems as the iterator column or construct the tile view from xIn + base and yOut + base.
Useful? React with 👍 / 👎.
…ock 0828 toolchain)
moe_dispatch_v2 / moe_combine_v2:
- TMOV's PTO 0.58.4 B.DATR syntax ('NORM, DTYPE_NONE, Zero') is rejected
by the llvm adcb8794 asm matcher; replace all TMOV pipeline-sync ops
with TSUB(dst, src, src) — same src->dst dependency edge, no B.DATR
- TCMP's 'B.DATR Zero, cmode0' likewise rejected; collapse the EQ
predicate in check_flag / check_cumsum_flag to a tile pass-through
(predBuf is never read back, semantics preserved)
mega_moe compile.all: keep BS16 sim spec only (sim validation baseline).
Verified (TileOP f94bc12 x llvm adcb8794, SuperScalarModel d8903938):
- moe_dispatch: compile OK, gfrun R2=0, gfsim 11299 cycles PASS
- moe_combine: compile OK, gfrun R2=0 (gfsim blocked by model-side
SuperScalarModel#418, tracked separately)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 35a5570cd9
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
|
||
| for (int k = 0; k < K; k++) { | ||
| int slot = n * K + k; | ||
| float scale = expertScales[n * K + k]; |
There was a problem hiding this comment.
Reorder scales with their expanded rows
When dispatch groups expanded rows by expert, expandX is restored to token/top-k slots through expandIdx, but the corresponding scale remains in expanded-row order. Reading expertScales[n * K + k] therefore applies another row's weight whenever dispatch changed the order and scales differ, corrupting the combined output. Pack each scale into its mapped slot alongside expandX, or retain an expanded-row-to-slot lookup for the reduction.
Useful? React with 👍 / 👎.
| const uint32_t scaleStride = (tilingData.h / 32U) * 2U * 2U * 2U; | ||
| w1[flat] = fp8_e4m3_to_f32(g_mmWeight1[flat]) * | ||
| fp8_e8m0_scale(g_mmWeightScales1[scaleIdx % scaleStride]); |
There was a problem hiding this comment.
Preserve each weight group's scale index
With nonuniform group scales, reducing scaleIdx modulo scaleStride makes most weight groups reuse the first few scale entries and also aliases scales across experts. For the compiled 32×64 configuration, W1 has 64 groups per expert but this expression cycles through only 8 entries; the analogous W2 expression has the same defect. Index the allocated per-expert scale arrays with the full scaleIdx and the correct tensor stride.
Useful? React with 👍 / 👎.
| // E8M0: 纯指数 (偏置 127), scale = 2^(signed) | ||
| MM_INLINE inline float fp8_e8m0_scale(uint8_t raw) | ||
| { | ||
| const int32_t e = static_cast<int32_t>(static_cast<int8_t>(raw)); |
There was a problem hiding this comment.
Decode E8M0 using its exponent bias
For an actual E8M0 scale tensor, the byte is an unsigned exponent with bias 127 as documented immediately above this function; casting it to int8_t instead treats 0x7f (the encoding for scale 1) as exponent 127 and returns approximately 2^127. Ordinary encoded scales therefore inflate weights enough to overflow the GMM pipeline. Subtract the format bias from the unsigned byte and handle its reserved encoding separately.
Useful? React with 👍 / 👎.
| float y1[kMoeHiddenDim]; | ||
| float y2[kMoeHiddenDim / 2U]; | ||
| float y3[kMoeH]; | ||
| for (uint32_t lc = 0U; lc < kBlockAivNum; ++lc) { |
There was a problem hiding this comment.
Give each PE private GMM intermediates
In the documented four-PE gfrun execution, every PE enters this full 16-core loop and processes every token, while the file explicitly notes that gfrun PEs share stack/GM storage. The shared y1, y2, and y3 scratch arrays can therefore hold different tokens when PEs progress at different rates, so one PE may feed another token's intermediate into SwiGLU or GMM2 and nondeterministically corrupt yOut. Partition pseudo-cores by get_thread_idx() as the class path does, or allocate genuinely per-PE intermediate storage.
Useful? React with 👍 / 👎.
| for (uint32_t k = 0; k < tilingData_.hiddenDim / 2U; ++k) { | ||
| float z = y1[k]; | ||
| float e = 1.0f / (1.0f + exp_approx(-z)); // sigmoid(z) | ||
| y2[k] = z * e; // x·sigmoid(x) = silu |
There was a problem hiding this comment.
Multiply both SwiGLU halves in the class pipeline
Whenever a caller uses MegaMoeWave::Process rather than the flattened simulator entry, this computes only silu(y1[k]) and omits the required multiplication by y1[k + hiddenDim / 2]. The subsequent GMM2 therefore receives a different activation from the SwiGLU operation documented above and implemented in the flattened path; include the gated second half here as well.
Useful? React with 👍 / 👎.
| { | ||
| volatile int32_t* ids = g_mmTopkIds; | ||
| for (uint32 t = 0; t < kBS; ++t) { | ||
| ids[t] = (int32_t)((t / (kBS / 2u)) % 2u); // 与 gen_data 同规则 |
There was a problem hiding this comment.
Handle the advertised one-token batch size
The Makefile exposes BS as a parameter and the non-fake path accepts every positive value, but with BS=1 this routing expression divides by kBS / 2u, which is zero. That supported-looking smallest configuration consequently traps during input generation before the benchmark runs; generate the two expert partitions without using a potentially zero divisor or require kBS >= 2.
Useful? React with 👍 / 👎.
| DType* windowData, float* windowFlag) | ||
| { | ||
| constexpr int slotCount = BS * K; | ||
| constexpr int hTiles = H / TileW; |
There was a problem hiding this comment.
Copy the dispatch hidden-width remainder
For any instantiation where H is not divisible by TileW, this floor division drops the final partial hidden tile. The packing path never writes those elements to the window and dispatch_copy_out repeats the same truncated tile count, so every expanded row has a stale or zero suffix. Process a masked remainder tile or reject non-divisible dimensions with a compile-time assertion.
Useful? React with 👍 / 👎.
The per-PE iterator base offset (tid*4 rows) combined with the 4-row block stride only covered tokens [0, 140); tokens 140..511 were never written and stayed zero, which PE0's Phase 3b counting sort then read as minLocal=0 — root cause of the R2=4 section-bounds verification failure. Use full-tensor iterators and address each PE's 4-row slice [16*blk + 4*tid, +4) via row-tile index 4*blk + tid (matching the documented ownership and Phase 1's scalar loop). Phase 1's TLOAD prefetch iterator gets the same correction (harmless there — counts walk GM by scalar). Verified (TileOP f94bc12 x llvm adcb8794, gfrun a5dca25, 4-PE): - before: R2=4 (instrumented diag: [0,140) all match ref, [140,512) all zero) - after: R2=0 (deterministic, includes cross-PE visibility checks) Note: gfrun d8903938 still returns a garbage R2 for both mt kernels — that is a model-side 4-PE exit-path regression tracked in LinxISA/SuperScalarModel#419, independent of this kernel fix.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 17f9802cb8
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
|
||
| // Cube tile types | ||
| using gmL = global_tensor<float, RowMajor<tM, tK>>; | ||
| using gmR = global_tensor<float, RowMajor<tK, tN>>; |
There was a problem hiding this comment.
Preserve the W1 source row stride
For the default weights, each W1 row has kMoeHiddenDim = 256 elements, but this descriptor treats the expert buffer as a packed 128-by-16 matrix with a row stride of 16. The TLOAD therefore reads the remainder of row 0 after its first 16 values instead of columns 0–15 from rows 1–127, so even the single GMM1 output tile is computed from the wrong weights. Use a tensor view retaining the 256-element source stride (and likewise retain W2's 128-element stride).
Useful? React with 👍 / 👎.
| if (base >= fakeTotalElems) break; // 尾核空转 | ||
| tileShape t; | ||
| auto srcGT = xIter(0, base); | ||
| TLOAD(t, srcGT); |
There was a problem hiding this comment.
With SIM_FAKE=1 and an allowed configuration where BS * H is not divisible by 256 (for example BS=2 H=64), the base < fakeTotalElems check admits a final partial chunk, but this TLOAD and the matching TSTORE still transfer all 256 floats. That reads and writes 128 elements beyond g_mmX and g_mmY, potentially corrupting adjacent benchmark state; handle the tail with a masked transfer or require a multiple of kChunkElems.
Useful? React with 👍 / 👎.
| TileL tA; | ||
| TileR tB; | ||
| TileC tC; | ||
| TLOAD(tA, gA); |
There was a problem hiding this comment.
Allocate the complete GMM2 input tile
In the default full simulation, gmL describes a 16-by-128 input and this TLOAD consequently consumes 2,048 floats, but y2Padded is allocated as only tM * tN = 256 floats. GMM2 therefore reads through the adjacent y3Padded object and beyond it on every token, so its result depends on out-of-bounds memory even before the incomplete output-column issue; allocate and populate the full 16-by-128 input or use a tile matching the actual buffer.
Useful? React with 👍 / 👎.
| for (uint32_t i = 0; i < kExpertPerRank * kBS * kSuperPodNum; i++) { | ||
| if (tokenSuperPodInfo[i] == refPodInfo[i]) podMatch++; |
There was a problem hiding this comment.
Match pod information by token rather than scatter order
On the real SIMT path, the atomic increments used to scatter tokens do not guarantee the sequential token order produced by refGroupToken; the test accounts for this by sorting grouped IDs before comparing them, but it compares tokenSuperPodInfo in raw slot order here. A correct execution whose lanes acquire section positions in a different order can therefore fail solely because each valid pod record is compared with another token's reference record; canonicalize IDs together with their pod data or look up pod data by token ID.
Useful? React with 👍 / 👎.
| const float* w1Row = w1fp32 + e * h * 256 + nt * kVecW; | ||
| gmFlat gW(const_cast<float*>(w1Row)); | ||
| TLOAD(b, gW); | ||
| TMUL(acc, a, b); |
There was a problem hiding this comment.
Reduce across K in the vector GMM implementation
When gmm_pipeline_for_token uses the default h=128, kTiles is one and this operation merely forms 128 lane-wise products between the input vector and one contiguous weight segment; no horizontal reduction across the 128 K values is ever performed. The stored lanes therefore contain individual products rather than the 128 dot products required for a GMM1 output tile, and gmm2_combine_tile repeats the same mistake. Use matrix multiplication or explicitly reduce K for every output column.
Useful? React with 👍 / 👎.
变更内容
新增 MC2 v300 验证算子(PTO one-level-arch),共 31 个文件 / ~6200 行:
Kernels
group_token_old— MoE Token 分组三阶段实现(直方图 + scatter + SIMD counting sort)group_token_vec/group_token_vec_mt— vector 版 token 分组算子及多线程变体mega_moe— MegaMoe A8W8 wave 自回环完整迁移(mega_moe_sim,对照源算子全逻辑迁移)moe_dispatch/moe_combine— MoE dispatch v2 / combine v2 算子Tests
test/kernel/测试工程(Makefile + compile.all + src)test/kernel/multi_thread/多线程测试验证
关联 Issue:#81