a2a_gemm examples with flydsl + mori#450
Conversation
|
The SDMA+GEMM overlap rests on one hardware fact: on gfx950 the SDMA copy engines are separate hardware from the compute units (CUs). So a data transfer can run on the DMA engine at the same time as a GEMM runs on the CUs — if you can (a) launch the transfer without occupying CUs, and (b) let the GEMM start consuming data before the whole transfer finishes. 1. CU-free transfer (the enabler)
2. The flag handshake (correctness + pipelining)The transfer and the GEMM are decoupled, so they synchronize through flags:
That per-chunk granularity is what turns "transfer, then compute" into an overlapped pipeline: 3. Two ways to express the wait
Either way, the overlap is the DMA engine ∥ CUs, not two kernels racing. 4. When it actually pays (the limits we measured)Overlap hides
In one sentenceIssue the transfer on the CU-free SDMA engine and return; the GEMM computes each tile as its per-chunk flag fires, so compute runs on the CUs while the remaining data streams in on the DMA engine — bounded by whether the comm is CU-free, whether the GEMM is big enough to hide it, and whether the transfer is large enough to beat nccl's overhead. |
|
Thanks for the great work! Using device-initiated SDMA (putmem_nbi_signal) to run the A2A on the CU-free copy engines while the GEMM keeps the CUs busy is a really nice idea, and the in-kernel per-tile gate is a clean way to express it. On why the overlap gain looks marginal here — I think three things compound:
A large-GEMM or ≥8-GPU data point would help show where it actually pays off. One impl note: the SDMA transport is currently selected at runtime (transportTypes[pe] branch), not compile-time — for a fine-grained per-tile push loop that runtime dispatch may add hot-path overhead. On our side we plan to use CCO (shmem v2) for explicit compile-time calls to remove this kind of overhead. I think this is a valuable reference for the fused-comm pattern. |
Self-contained example demonstrating how to overlap an All-to-All collective with
the bf16 GEMM that consumes it, using device-initiated SDMA (mori putmem_nbi_signal)
so the transfer runs on the CU-free DMA copy engines while the GEMM runs on the CUs.
Models a sequence-parallel "A2A -> projection" step (P = ranks): each rank holds
[world*sw, Hp], an All-to-All redistributes it to chunk-major a_full[P, sw, Hp],
then a split-K projection GEMM computes out = sum_i a_full[i] @ W[i].T. Compares
unfused (nccl), seq (SDMA, no overlap), and fused (SDMA + per-source gate+GEMM
overlap), with a correctness check.
The FlyDSL SDMA producer + per-source gate kernels are bundled in the single file
(no external project deps beyond the flydsl/mori/aiter runtime). Generic dims only.
Ran 4-rank on a gfx950 node via the flydsl/mori/aiter runtime:
Output (correct=True both sizes):
python3 -m py_compile passes; arc lint -a clean (BLACK).