Blockwise-scaled split-K GEMM applies slice-0 scale factors to every K slice
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 10.5k
- Forks
- 2.1k
- Avg merge
- 3d 11h
- Merged PRs (30d)
- 7
Description
Description
Blockwise-scaled GEMM applies the wrong scale factors for every split-K slice beyond the first: the scale lookup uses the CTA-local K-tile index and is never offset by the slice's position in the global K range.
The multistage mainloop indexes the scale tensors by k_iter_idx, the loop counter local to this threadblock:
// include/cutlass/gemm/threadblock/mma_multistage_blockwise.h:243-250
int ldA = int(scale_A.layout().stride(0));
int k_block_idx = k_iter_idx;
if (k_block_idx >= ldA) {
k_block_idx = ldA - 1; // clamps OOB reads only
}
float scale_factor = scale_A.at({block_m_idx, k_block_idx}) *
scale_B.at({block_n_idx, k_block_idx});
and the kernel passes only the M/N tile offsets down to the MMA:
// include/cutlass/gemm/kernel/gemm_universal_blockwise.h:268-273
mma(gemm_k_iterations, accumulators, iterator_A, iterator_B, accumulators,
params.scale_A, params.scale_B, threadblock_tile_offset.m(),
threadblock_tile_offset.n());
There is no threadblock_tile_offset.k() contribution anywhere, so slice s (which multiplies A/B blocks from global K range [s * tiles_per_slice, (s+1) * tiles_per_slice)) scales all of its partial products with the scale factors of K-blocks [0, ...). The clamp at line 245 turns out-of-range lookups into silent reuse of the last scale block instead of a diagnostic.
The wrapper accepts this configuration when kSplitKSerial is true (device/gemm_blockwise.h:367-369 rejects split_k_slices > 1 only for the non-serial path), so the mis-scaled result is returned as success.
Suggested fix
Offset the scale index by the slice's global K-tile base (threadblock_tile_offset.k() * gemm_k_size / ThreadblockShape::kK, or equivalent) before indexing scale_A/scale_B, keeping the existing clamp as a bounds guard.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in include/cutlass/gemm/threadblock/mma_multistage_blockwise.h and trace how include/cutlass/gemm/kernel/gemm_universal_blockwise.h passes the tile offsets into the MMA. Then inspect the serial split-K acceptance path in device/gemm_blockwise.h. Done means split-K slices use their global K-tile base for scale lookup while retaining the existing bounds clamp, without changing unsplit behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 64/100