Skip to content

Commit d335c03

Browse files
authored
feat(EPv2): extend support for rack-level wide-ep. (#597)
1 parent 404255a commit d335c03

5 files changed

Lines changed: 50 additions & 33 deletions

File tree

python/mori/jit/config.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,20 @@ def detect_gpu_arch(rocm_path: str = "/opt/rocm") -> str:
9090
)
9191

9292

93+
def detect_wave_size() -> int:
94+
"""Return the wavefront width for the current GPU (32 or 64).
95+
96+
Override with MORI_WAVE_SIZE. Falls back to 64 when detection fails.
97+
"""
98+
v = os.environ.get("MORI_WAVE_SIZE")
99+
if v:
100+
return int(v)
101+
try:
102+
return 32 if detect_gpu_arch().startswith("gfx12") else 64
103+
except Exception:
104+
return 64
105+
106+
93107
def _find_tool(rocm_path: str, name: str) -> str:
94108
"""Locate a ROCm LLVM tool, raising FileNotFoundError if missing."""
95109
candidates = [

python/mori/ops/dispatch_combine_v2/dispatch_combine_op.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@
5050
import torch
5151

5252
from mori.tensor_utils import from_gpu_ptr
53+
from mori.jit.config import detect_wave_size
54+
55+
WAVE = detect_wave_size()
5356

5457
# Where each backend lives. Imported lazily, on selection only.
5558
_BACKEND_MODULES = {"flydsl": "flydsl_backend", "hip": "hip_backend"}
@@ -128,12 +131,6 @@ def __post_init__(self):
128131
)
129132
if self.quant_type != "none":
130133
self.combine_mode = "scatter"
131-
# The dispatch grid barrier resets inside a `range(lane, npes, 64)` loop,
132-
# correct only while each lane runs it once (npes <= wavefront).
133-
if self.world_size > 64:
134-
raise ValueError(
135-
f"intranode op supports world_size <= 64, got {self.world_size}"
136-
)
137134
# Token copy moves whole 16 B (vec4) chunks; a non-16 B-aligned per-token
138135
# size would over-read/write a few dwords past the token.
139136
if self.token_nbytes % 16 != 0:
@@ -233,6 +230,22 @@ def _resolve_geometry(self):
233230
if self.combine_warp_num_per_block is None:
234231
self.combine_warp_num_per_block = 4
235232

233+
# Precise world_size check against the resolved geometry. The combine
234+
# xdb barrier polls with `tid < npes`, so every schedule bucket must
235+
# have blockDim (= comb_warp * WAVE) >= world_size.
236+
if self.schedule:
237+
min_comb_warp = min(bucket[4] for bucket in self.schedule)
238+
else:
239+
min_comb_warp = self.combine_warp_num_per_block
240+
max_peers = min_comb_warp * WAVE
241+
if self.world_size > max_peers:
242+
raise ValueError(
243+
f"world_size ({self.world_size}) exceeds the smallest combine "
244+
f"blockDim in the schedule ({min_comb_warp} warps × {WAVE}-wide "
245+
f"wave = {max_peers} threads); the `tid < npes` barrier requires "
246+
f"world_size <= blockDim"
247+
)
248+
236249
@property
237250
def is_scatter(self):
238251
return self.combine_mode == "scatter"

python/mori/ops/dispatch_combine_v2/intranode_kernels.py

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -72,25 +72,9 @@
7272

7373
from . import flydsl_prims as P
7474

75-
# Wavefront size: gfx9 (MI300/MI350) = 64, gfx12 (MI400/gfx1250) = 32. Detected
76-
# once per process; override with MORI_WAVE_SIZE. get_warp_size(gfx1250) wrongly
77-
# reports 64, so key off the arch string.
78-
import os as _os
75+
from mori.jit.config import detect_wave_size
7976

80-
81-
def _detect_wave_size():
82-
v = _os.environ.get("MORI_WAVE_SIZE")
83-
if v:
84-
return int(v)
85-
try:
86-
from mori.jit.config import detect_gpu_arch
87-
88-
return 32 if detect_gpu_arch().startswith("gfx12") else 64
89-
except Exception:
90-
return 64
91-
92-
93-
WAVE = _detect_wave_size()
77+
WAVE = detect_wave_size()
9478
LANE_MASK = WAVE - 1
9579
LOG2_WAVE = WAVE.bit_length() - 1
9680
_BALLOT_INT = T.i64 if WAVE == 64 else T.i32
@@ -348,10 +332,11 @@ def ep_dispatch(
348332
P.atomic_add_global(fx.Int64(addr_disp_bar), arith.constant(1))
349333

350334
local_recv_num = fx.Int64(window.lsa_ptr(my_lsa_rank, off_recv_num))
335+
if global_warp_id == 0:
336+
P.spin_until_eq_i32(fx.Int64(addr_disp_bar), block_num)
337+
buffer_store(arith.constant(0), rsrc_disp_bar, 0)
351338
for dest_pe in range(lane, npes, WAVE):
352339
if global_warp_id == 0:
353-
P.spin_until_eq_i32(fx.Int64(addr_disp_bar), block_num)
354-
buffer_store(arith.constant(0), rsrc_disp_bar, 0)
355340
signal_value = (
356341
buffer_load(rsrc_dest_ctr, dest_pe, vec_width=1, dtype=T.i32())
357342
+ 1

src/ops/dispatch_combine_v2/ep_intranode_1250x.hpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,11 @@ using index_t = int32_t;
6363
#define MORI_COMB_BARSLEEP 15
6464
#define MORI_COMB_BARSPREAD 16
6565

66-
#define CUSPLIT_MAX_GPUS 8
66+
// MORI_EP_WORLD_SIZE is emitted by RenderEpSource before #include-ing this
67+
// header, so the global arrays below are sized to the exact config.
68+
#ifndef MORI_EP_WORLD_SIZE
69+
#define MORI_EP_WORLD_SIZE 8
70+
#endif
6771

6872
template <typename T>
6973
__device__ __forceinline__ uint32_t MoriPackTo2(float a, float b) {
@@ -199,15 +203,15 @@ __device__ __forceinline__ gfx1250_TDM_GROUP1 TdmSplitShape(const TdmSplit128& s
199203
return TdmShape2D(32, sp.rows);
200204
}
201205

202-
#define CUSPLIT_POOL_SLOTS (CUSPLIT_MAX_GPUS * 32768)
206+
#define CUSPLIT_POOL_SLOTS (MORI_EP_WORLD_SIZE * 32768)
203207
#define CUSPLIT_MAX_BLOCKS 512
204208
#define CUSPLIT_MAX_TOPK 16
205209

206210
__device__ index_t _cusplit_stgIdx[CUSPLIT_POOL_SLOTS * CUSPLIT_MAX_TOPK];
207211
__device__ float _cusplit_stgWt[CUSPLIT_POOL_SLOTS * CUSPLIT_MAX_TOPK];
208212
__device__ index_t _cusplit_stgSrc[CUSPLIT_POOL_SLOTS];
209-
__device__ index_t _cusplit_blkBase[CUSPLIT_MAX_BLOCKS * CUSPLIT_MAX_GPUS];
210-
__device__ index_t _cusplit_blkCount[CUSPLIT_MAX_BLOCKS * CUSPLIT_MAX_GPUS];
213+
__device__ index_t _cusplit_blkBase[CUSPLIT_MAX_BLOCKS * MORI_EP_WORLD_SIZE];
214+
__device__ index_t _cusplit_blkCount[CUSPLIT_MAX_BLOCKS * MORI_EP_WORLD_SIZE];
211215
// Per-token scale rows, staged like the other meta fields so they ship to a peer as
212216
// one contiguous run rather than a 224 B transfer per (token, destination) -- the
213217
// size TDM is worst at. The array is at file scope, which the TU reaches before kCfg
@@ -299,7 +303,7 @@ __device__ void EpDispatch1250xBody(EpArgs args) {
299303
T* _tdmTile = reinterpret_cast<T*>(_tdmBatchSmem + (size_t)warpId * kSlabBytes);
300304
const gfx1250_TDM_GROUP1 _tdmG1 = TdmShape<T>(static_cast<int>(hiddenDim));
301305

302-
constexpr int kMaxNpes = CUSPLIT_MAX_GPUS;
306+
constexpr int kMaxNpes = kCfg.worldSize;
303307
__shared__ index_t s_N[kMaxNpes];
304308
__shared__ index_t s_base[kMaxNpes];
305309
__shared__ index_t s_run[kMaxNpes];

src/ops/dispatch_combine_v2/ep_spec.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,9 @@ std::string RenderEpSource(const EpCfg& cfg, const std::string& entry, const cha
162162
"\n#define MORI_EP_SCALE_SLOTS " +
163163
std::to_string((long long)cfg.worldSize * EpMaxRecv(cfg)) + "\n";
164164
}
165-
return std::string("// mori jit v2 — generated, do not edit.\n") + scaleDefs + "#include \"" +
166-
header +
165+
std::string worldDef = "#define MORI_EP_WORLD_SIZE " + std::to_string(cfg.worldSize) + "\n";
166+
return std::string("// mori jit v2 — generated, do not edit.\n") + worldDef + scaleDefs +
167+
"#include \"" + header +
167168
"\"\n"
168169
"using namespace mori::ops::v2;\n"
169170
"constexpr EpCfg kCfg = " +

0 commit comments

Comments
 (0)