Skip to content

Commit f73fc14

Browse files
Merge branch 'main' into akaratza_shadow_test_new_mi300_vluster
2 parents 1435ca3 + 1ef1c7e commit f73fc14

78 files changed

Lines changed: 2388 additions & 1050 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.buildkite/test_areas/models_basic.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ steps:
2727
# subset of supported models (the complement of the small subset in the above
2828
# test.) Also run if model initialization test file is modified
2929
- pytest -v -s models/test_initialization.py -k 'not test_can_initialize_small_subset' --num-shards=$$BUILDKITE_PARALLEL_JOB_COUNT --shard-id=$$BUILDKITE_PARALLEL_JOB
30-
parallelism: 2
30+
parallelism: 4
3131

3232
- label: Basic Models Tests (Other)
3333
device: h200_35gb

.buildkite/test_areas/models_multimodal.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ steps:
7373
depends_on:
7474
- image-build-amd
7575

76-
- label: Multi-Modal Processor (CPU)
76+
- label: Multi-Modal Processor (CPU) %N
7777
key: multi-modal-processor-cpu
7878
depends_on:
7979
- image-build-cpu
@@ -84,7 +84,8 @@ steps:
8484
- tests/models/registry.py
8585
device: cpu-medium
8686
commands:
87-
- pytest -v -s models/multimodal/processing --ignore models/multimodal/processing/test_tensor_schema.py
87+
- pytest -v -s models/multimodal/processing --ignore models/multimodal/processing/test_tensor_schema.py --num-shards=$$BUILDKITE_PARALLEL_JOB_COUNT --shard-id=$$BUILDKITE_PARALLEL_JOB
88+
parallelism: 4
8889

8990
- label: Multi-Modal Processor # 44min
9091
key: multi-modal-processor

cmake/external_projects/flashmla.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ else()
1919
FetchContent_Declare(
2020
flashmla
2121
GIT_REPOSITORY https://github.com/vllm-project/FlashMLA
22-
GIT_TAG a6ec2ba7bd0a7dff98b3f4d3e6b52b159c48d78b
22+
GIT_TAG b70aff3d110a2b1a037e62eac295166b5143643a
2323
GIT_PROGRESS TRUE
2424
CONFIGURE_COMMAND ""
2525
BUILD_COMMAND ""

cmake/external_projects/vllm_flash_attn.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ else()
3939
FetchContent_Declare(
4040
vllm-flash-attn
4141
GIT_REPOSITORY https://github.com/vllm-project/flash-attention.git
42-
GIT_TAG b3964b1d8b95d8e8447435668ab169a2700bab65
42+
GIT_TAG bb9a72e7dde0dc614ffc663e052cd6a19ce73a42
4343
GIT_PROGRESS TRUE
4444
# Don't share the vllm-flash-attn build between build types
4545
BINARY_DIR ${CMAKE_BINARY_DIR}/vllm-flash-attn

csrc/libtorch_stable/fp32_router_gemm.cu

Lines changed: 179 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
// SPDX-License-Identifier: Apache-2.0
22
// SPDX-FileCopyrightText: Copyright contributors to the vLLM project
33
//
4-
// Router GEMM: activation(T) x weight(fp32) -> fp32, H=3072, E=256, M<=32.
4+
// Router GEMM: activation(T) x weight(fp32) -> fp32, M<=32, for the
5+
// supported (E, H) pairs listed at the bottom of this file.
56
// Supports bf16 or fp32 activation; weight is always fp32.
67
// Adapted from dsv3_router_gemm_float_out.cu.
8+
// (E=256, H=6144) bf16 uses a B300-tuned wide-block geometry; see
9+
// invokeFp32RouterGemm.
710

811
#include <cuda_bf16.h>
912
#include <cuda_runtime.h>
1013

14+
#include <type_traits>
15+
1116
// ---------------------------------------------------------------------------
1217
// Load helpers
1318
// ---------------------------------------------------------------------------
@@ -73,110 +78,226 @@ __device__ __forceinline__ void load_activation<__nv_bfloat16, 8>(
7378
// InputT : type of activation (float or __nv_bfloat16)
7479
// Weight is always fp32; output is always fp32.
7580
// VPT = 16 / sizeof(InputT): 4 for fp32, 8 for bf16
76-
template <typename InputT, int kBlockSize, int kNumTokens, int kNumExperts,
77-
int kHiddenDim>
78-
__global__ __launch_bounds__(128, 1) void fp32_router_gemm_kernel(
79-
float* out, InputT const* mat_a, float const* mat_b) {
81+
// Each block computes kEPB expert columns; wider blocks / kEPB > 1 are
82+
// selected per (shape, M) in invokeFp32RouterGemm (B300-tuned, see below).
83+
// kTGroups > 1 splits the tokens across groups of kBlockSize threads within
84+
// the block: all groups scan the same weight K-slices (group 0 misses to
85+
// DRAM, later groups hit L1) so weight traffic stays 1x, while per-thread
86+
// accumulator registers drop by kTGroups (at M=16 the 32 fp32 accumulators
87+
// push the kernel to 128 regs/thread and 1 block/SM).
88+
template <typename InputT, int kBlockSize, int kNumTokens, int kEPB,
89+
int kNumExperts, int kHiddenDim, int kTGroups = 1>
90+
__global__ __launch_bounds__(
91+
kBlockSize* kTGroups, 1) void fp32_router_gemm_kernel(float* out,
92+
InputT const* mat_a,
93+
float const* mat_b) {
8094
constexpr int VPT = 16 / sizeof(InputT);
8195
constexpr int k_elems_per_k_iteration = VPT * kBlockSize;
8296
constexpr int k_iterations = kHiddenDim / k_elems_per_k_iteration;
97+
static_assert(kHiddenDim % k_elems_per_k_iteration == 0);
98+
static_assert(kNumTokens % kTGroups == 0);
8399
constexpr int kWarpSize = 32;
84-
constexpr int kNumWarps = kBlockSize / kWarpSize;
100+
constexpr int kNumWarps = kBlockSize / kWarpSize; // per token group
101+
constexpr int kMG = kNumTokens / kTGroups; // tokens per group
85102

86-
int const n_idx = blockIdx.x;
87-
int const tid = threadIdx.x;
103+
int const e_base = blockIdx.x * kEPB;
104+
int const tid = threadIdx.x % kBlockSize;
105+
int const m0 = (threadIdx.x / kBlockSize) * kMG;
88106
int const warpId = tid / kWarpSize;
89107
int const laneId = tid % kWarpSize;
90108

91-
float acc[kNumTokens] = {};
92-
__shared__ float sm_reduction[kNumTokens][kNumWarps];
93-
94-
float const* b_col = mat_b + n_idx * kHiddenDim;
95-
96-
int k_bases[k_iterations];
97-
#pragma unroll
98-
for (int ki = 0; ki < k_iterations; ki++) {
99-
k_bases[ki] = ki * k_elems_per_k_iteration + tid * VPT;
100-
}
109+
float acc[kMG][kEPB] = {};
110+
__shared__ float sm_reduction[kNumTokens][kEPB][kNumWarps];
101111

102112
#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900)
103113
cudaGridDependencySynchronize();
114+
// Fire the PDL trigger right after our own wait instead of at kernel end:
115+
// a gridsync-ing consumer is unaffected (its wait always targets full grid
116+
// completion), while a consumer that reads none of our outputs (e.g. the
117+
// NVFP4 activation quant, which reads the same hidden_states) can launch
118+
// now and fully overlap this kernel's body.
119+
cudaTriggerProgrammaticLaunchCompletion();
104120
#endif
105121

122+
#pragma unroll
106123
for (int ki = 0; ki < k_iterations; ki++) {
107-
int const k_base = k_bases[ki];
124+
int const k_base = ki * k_elems_per_k_iteration + tid * VPT;
108125

109-
float b_float[VPT];
110-
load_weight<VPT>(b_col + k_base, b_float);
126+
float b_float[kEPB][VPT];
127+
#pragma unroll
128+
for (int e = 0; e < kEPB; e++) {
129+
load_weight<VPT>(mat_b + (e_base + e) * kHiddenDim + k_base, b_float[e]);
130+
}
111131

112132
#pragma unroll
113-
for (int m_idx = 0; m_idx < kNumTokens; m_idx++) {
133+
for (int m_idx = 0; m_idx < kMG; m_idx++) {
114134
float a_float[VPT];
115-
load_activation<InputT, VPT>(mat_a + m_idx * kHiddenDim + k_base,
116-
a_float);
135+
load_activation<InputT, VPT>(
136+
mat_a + (size_t)(m0 + m_idx) * kHiddenDim + k_base, a_float);
137+
#pragma unroll
138+
for (int e = 0; e < kEPB; e++) {
117139
#pragma unroll
118-
for (int k = 0; k < VPT; k++) {
119-
acc[m_idx] += a_float[k] * b_float[k];
140+
for (int k = 0; k < VPT; k++) {
141+
acc[m_idx][e] += a_float[k] * b_float[e][k];
142+
}
120143
}
121144
}
122145
}
123146

124147
// Warp-level butterfly reduction
125148
#pragma unroll
126-
for (int m = 0; m < kNumTokens; m++) {
127-
float sum = acc[m];
128-
sum += __shfl_xor_sync(0xffffffff, sum, 16);
129-
sum += __shfl_xor_sync(0xffffffff, sum, 8);
130-
sum += __shfl_xor_sync(0xffffffff, sum, 4);
131-
sum += __shfl_xor_sync(0xffffffff, sum, 2);
132-
sum += __shfl_xor_sync(0xffffffff, sum, 1);
133-
if (laneId == 0) sm_reduction[m][warpId] = sum;
149+
for (int m = 0; m < kMG; m++) {
150+
#pragma unroll
151+
for (int e = 0; e < kEPB; e++) {
152+
float sum = acc[m][e];
153+
sum += __shfl_xor_sync(0xffffffff, sum, 16);
154+
sum += __shfl_xor_sync(0xffffffff, sum, 8);
155+
sum += __shfl_xor_sync(0xffffffff, sum, 4);
156+
sum += __shfl_xor_sync(0xffffffff, sum, 2);
157+
sum += __shfl_xor_sync(0xffffffff, sum, 1);
158+
if (laneId == 0) sm_reduction[m0 + m][e][warpId] = sum;
159+
}
134160
}
135161

136162
__syncthreads();
137163

138-
if (tid == 0) {
139-
#pragma unroll
140-
for (int m = 0; m < kNumTokens; m++) {
141-
float final_sum = 0.0f;
164+
// Parallel finalize: one thread per (m, e) output.
165+
for (int idx = threadIdx.x; idx < kNumTokens * kEPB;
166+
idx += kBlockSize * kTGroups) {
167+
int const m = idx / kEPB;
168+
int const e = idx % kEPB;
169+
float final_sum = 0.0f;
142170
#pragma unroll
143-
for (int w = 0; w < kNumWarps; w++) final_sum += sm_reduction[m][w];
144-
out[m * kNumExperts + n_idx] = final_sum;
145-
}
171+
for (int w = 0; w < kNumWarps; w++) final_sum += sm_reduction[m][e][w];
172+
out[m * kNumExperts + e_base + e] = final_sum;
146173
}
147-
148-
#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900)
149-
cudaTriggerProgrammaticLaunchCompletion();
150-
#endif
151174
}
152175

153176
// ---------------------------------------------------------------------------
154177
// Launcher
155178
// ---------------------------------------------------------------------------
156179

157-
template <typename InputT, int kNumTokens, int kNumExperts, int kHiddenDim>
158-
void invokeFp32RouterGemm(float* output, InputT const* mat_a,
159-
float const* mat_b, cudaStream_t stream) {
160-
constexpr int kBlockSize = 128;
180+
template <typename InputT, int kBlockSize, int kEPB, int kNumTokens,
181+
int kNumExperts, int kHiddenDim, int kTGroups = 1>
182+
static void launchFp32RouterGemm(float* output, InputT const* mat_a,
183+
float const* mat_b, cudaStream_t stream) {
184+
static_assert(kNumExperts % kEPB == 0);
161185
cudaLaunchConfig_t config;
162-
config.gridDim = kNumExperts;
163-
config.blockDim = kBlockSize;
186+
config.gridDim = kNumExperts / kEPB;
187+
config.blockDim = kBlockSize * kTGroups;
164188
config.dynamicSmemBytes = 0;
165189
config.stream = stream;
166190
cudaLaunchAttribute attrs[1];
167191
attrs[0].id = cudaLaunchAttributeProgrammaticStreamSerialization;
168192
attrs[0].val.programmaticStreamSerializationAllowed = 1;
169193
config.numAttrs = 1;
170194
config.attrs = attrs;
171-
cudaLaunchKernelEx(&config,
172-
fp32_router_gemm_kernel<InputT, kBlockSize, kNumTokens,
173-
kNumExperts, kHiddenDim>,
174-
output, mat_a, mat_b);
195+
cudaLaunchKernelEx(
196+
&config,
197+
fp32_router_gemm_kernel<InputT, kBlockSize, kNumTokens, kEPB, kNumExperts,
198+
kHiddenDim, kTGroups>,
199+
output, mat_a, mat_b);
200+
}
201+
202+
static bool isBlackwellFamily() {
203+
static int sm = []() {
204+
int dev = 0, major = 0, minor = 0;
205+
cudaGetDevice(&dev);
206+
cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, dev);
207+
cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, dev);
208+
return major * 10 + minor;
209+
}();
210+
return sm >= 100;
211+
}
212+
213+
template <typename InputT, int kNumTokens, int kNumExperts, int kHiddenDim>
214+
void invokeFp32RouterGemm(float* output, InputT const* mat_a,
215+
float const* mat_b, cudaStream_t stream) {
216+
// Geometry tuned on B300 per supported shape, bf16 activation, under a
217+
// production-fidelity harness (CUDA-graph replay, per-layer cold weights).
218+
// GLM-5.2 (E=256, H=6144):
219+
// M <= 4 : BS=768, EPB=1 (2.7us vs cast+cuBLAS 8.1us at M=1)
220+
// M in [5, 15]
221+
// or odd : BS=384, EPB=2 (crossover vs BS=768 measured in (4, 8))
222+
// M >= 16, even : BS=192, EPB=2, 2 token groups (M=16 4.79us vs 5.04,
223+
// M=24 5.71 vs 6.38, M=32 6.79 vs 7.72; M=12 loses at
224+
// 0.97x, so the boundary is 16).
225+
// Only enabled on the Blackwell family where it was validated; Hopper and
226+
// other shapes / fp32 activation keep the legacy geometry.
227+
if constexpr (std::is_same_v<InputT, __nv_bfloat16> && kNumExperts == 256 &&
228+
kHiddenDim == 6144) {
229+
if (!isBlackwellFamily()) {
230+
launchFp32RouterGemm<InputT, 128, 1, kNumTokens, kNumExperts, kHiddenDim>(
231+
output, mat_a, mat_b, stream);
232+
return;
233+
}
234+
if constexpr (kNumTokens <= 4) {
235+
launchFp32RouterGemm<InputT, 768, 1, kNumTokens, kNumExperts, kHiddenDim>(
236+
output, mat_a, mat_b, stream);
237+
} else if constexpr (kNumTokens >= 16 && kNumTokens % 2 == 0) {
238+
launchFp32RouterGemm<InputT, 192, 2, kNumTokens, kNumExperts, kHiddenDim,
239+
2>(output, mat_a, mat_b, stream);
240+
} else {
241+
launchFp32RouterGemm<InputT, 384, 2, kNumTokens, kNumExperts, kHiddenDim>(
242+
output, mat_a, mat_b, stream);
243+
}
244+
} else if constexpr (std::is_same_v<InputT, __nv_bfloat16> &&
245+
kNumExperts == 128 && kHiddenDim == 6144) {
246+
// MiniMax-M3. Legacy 128/1 only fills 128 blocks and pays the same
247+
// accumulator register cliffs; B300 sweep:
248+
// even M in [6, 10] : BS=384, EPB=1, 2 token groups (1.26-1.43x)
249+
// even M >= 12 : BS=192, EPB=1, 2 token groups (1.59-1.66x at
250+
// M >= 18; re-measured on B300+B200: 192 also wins
251+
// M=12/14 by 5-11%% on both, ties 384 at 16)
252+
// M <= 5 / odd : BS=384, EPB=1 (1.03-1.19x)
253+
if (!isBlackwellFamily()) {
254+
launchFp32RouterGemm<InputT, 128, 1, kNumTokens, kNumExperts, kHiddenDim>(
255+
output, mat_a, mat_b, stream);
256+
return;
257+
}
258+
if constexpr (kNumTokens >= 12 && kNumTokens % 2 == 0) {
259+
launchFp32RouterGemm<InputT, 192, 1, kNumTokens, kNumExperts, kHiddenDim,
260+
2>(output, mat_a, mat_b, stream);
261+
} else if constexpr (kNumTokens >= 6 && kNumTokens % 2 == 0) {
262+
launchFp32RouterGemm<InputT, 384, 1, kNumTokens, kNumExperts, kHiddenDim,
263+
2>(output, mat_a, mat_b, stream);
264+
} else {
265+
launchFp32RouterGemm<InputT, 384, 1, kNumTokens, kNumExperts, kHiddenDim>(
266+
output, mat_a, mat_b, stream);
267+
}
268+
} else if constexpr (std::is_same_v<InputT, __nv_bfloat16> &&
269+
kNumExperts == 256 && kHiddenDim == 3072) {
270+
// MiniMax-M2/M2.5. The 3.1MB weight is latency-floor bound at small M
271+
// (legacy already optimal); token groups win only at even M >= 8
272+
// (1.05-1.17x). EPB crossover measured between 12 and 16.
273+
if (!isBlackwellFamily()) {
274+
launchFp32RouterGemm<InputT, 128, 1, kNumTokens, kNumExperts, kHiddenDim>(
275+
output, mat_a, mat_b, stream);
276+
return;
277+
}
278+
if constexpr (kNumTokens >= 14 && kNumTokens % 2 == 0) {
279+
// M=14 originally measured 0.91x and stayed on legacy; two fresh
280+
// sweeps (B300 dev1 + B200) both put 192/2/tg2 ahead by 3.5-4%%.
281+
launchFp32RouterGemm<InputT, 192, 2, kNumTokens, kNumExperts, kHiddenDim,
282+
2>(output, mat_a, mat_b, stream);
283+
} else if constexpr (kNumTokens >= 8 && kNumTokens <= 12 &&
284+
kNumTokens % 2 == 0) {
285+
launchFp32RouterGemm<InputT, 192, 1, kNumTokens, kNumExperts, kHiddenDim,
286+
2>(output, mat_a, mat_b, stream);
287+
} else {
288+
launchFp32RouterGemm<InputT, 128, 1, kNumTokens, kNumExperts, kHiddenDim>(
289+
output, mat_a, mat_b, stream);
290+
}
291+
} else {
292+
launchFp32RouterGemm<InputT, 128, 1, kNumTokens, kNumExperts, kHiddenDim>(
293+
output, mat_a, mat_b, stream);
294+
}
175295
}
176296

177297
// ---------------------------------------------------------------------------
178298
// Explicit instantiations: M=1..32, for both input types, for the supported
179-
// (E, H) pairs: (256, 3072) [MiniMax-M2/M2.5] and (128, 6144) [MiniMax-M3].
299+
// (E, H) pairs: (256, 3072) [MiniMax-M2/M2.5], (128, 6144) [MiniMax-M3]
300+
// and (256, 6144) [GLM-5.2].
180301
// ---------------------------------------------------------------------------
181302

182303
#define INSTANTIATE(T, M, E, H) \
@@ -221,6 +342,8 @@ INSTANTIATE_ALL(float, 256, 3072)
221342
INSTANTIATE_ALL(__nv_bfloat16, 256, 3072)
222343
INSTANTIATE_ALL(float, 128, 6144)
223344
INSTANTIATE_ALL(__nv_bfloat16, 128, 6144)
345+
INSTANTIATE_ALL(float, 256, 6144)
346+
INSTANTIATE_ALL(__nv_bfloat16, 256, 6144)
224347

225348
#undef INSTANTIATE_ALL
226349
#undef INSTANTIATE

csrc/libtorch_stable/fp32_router_gemm_entry.cu

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@ inline int getSMVersion() {
2525
static constexpr int FP32_MAX_TOKENS = 32;
2626

2727
// Supported (hidden_dim, num_experts) pairs (must match the instantiations in
28-
// fp32_router_gemm.cu): (3072, 256) for MiniMax-M2/M2.5, (6144, 128) for M3.
28+
// fp32_router_gemm.cu): (3072, 256) for MiniMax-M2/M2.5, (6144, 128) for M3,
29+
// (6144, 256) for GLM-5.2.
2930
static inline bool fp32_router_gemm_supported(int hidden_dim, int num_experts) {
3031
return (hidden_dim == 3072 && num_experts == 256) ||
31-
(hidden_dim == 6144 && num_experts == 128);
32+
(hidden_dim == 6144 && num_experts == 128) ||
33+
(hidden_dim == 6144 && num_experts == 256);
3234
}
3335

3436
// Forward declarations — 4 template params must match fp32_router_gemm.cu
@@ -77,6 +79,9 @@ void dispatchFp32RouterGemm(int num_experts, int hidden_dim, int num_tokens,
7779
} else if (num_experts == 128 && hidden_dim == 6144) {
7880
Fp32LoopUnroller<InputT, 128, 6144, 1, FP32_MAX_TOKENS>::unroll(
7981
num_tokens, output, mat_a, mat_b, stream);
82+
} else if (num_experts == 256 && hidden_dim == 6144) {
83+
Fp32LoopUnroller<InputT, 256, 6144, 1, FP32_MAX_TOKENS>::unroll(
84+
num_tokens, output, mat_a, mat_b, stream);
8085
} else {
8186
throw std::invalid_argument(
8287
"fp32_router_gemm: unsupported (hidden_dim, num_experts) pair");
@@ -111,7 +116,7 @@ void fp32_router_gemm(
111116
STD_TORCH_CHECK(
112117
fp32_router_gemm_supported(hidden_dim, num_experts),
113118
"fp32_router_gemm: supported (hidden_dim, num_experts) pairs are "
114-
"(3072, 256) and (6144, 128)");
119+
"(3072, 256), (6144, 128) and (6144, 256)");
115120
STD_TORCH_CHECK(num_tokens <= FP32_MAX_TOKENS,
116121
"fp32_router_gemm: num_tokens must be in [0, 32]");
117122
STD_TORCH_CHECK(

0 commit comments

Comments
 (0)