main
cu 706 lines 27.4 KB
Raw
1 /*
2 * Modified by Neural Magic
3 * Copyright (C) Marlin.2024 Elias Frantar
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 /*
19 * Adapted from https://github.com/IST-DASLab/marlin
20 */
21
22 #ifndef MARLIN_NAMESPACE_NAME
23 #define MARLIN_NAMESPACE_NAME marlin_moe_wna16
24 #endif
25
26 #include "kernel.h"
27
28 #include <torch/csrc/stable/accelerator.h>
29 #include <torch/csrc/stable/library.h>
30 #include <torch/csrc/stable/ops.h>
31 #include <torch/csrc/stable/tensor.h>
32 #include <torch/headeronly/core/ScalarType.h>
33 #include <torch/headeronly/util/Exception.h>
34
35 #include "libtorch_stable/torch_utils.h"
36
37 #define STATIC_ASSERT_SCALAR_TYPE_VALID(scalar_t) \
38 static_assert(std::is_same<scalar_t, half>::value || \
39 std::is_same<scalar_t, nv_bfloat16>::value, \
40 "only float16 and bfloat16 is supported");
41
42 namespace MARLIN_NAMESPACE_NAME {
43
44 __global__ void MarlinDefault(MARLIN_KERNEL_PARAMS){};
45
46 using MarlinFuncPtr = void (*)(MARLIN_KERNEL_PARAMS);
47
48 typedef struct {
49 int thread_k;
50 int thread_n;
51 int num_threads;
52 } thread_config_t;
53
54 thread_config_t small_batch_thread_configs[] = {
55 // Ordered by priority
56
57 // thread_k, thread_n, num_threads
58 {128, 128, 256},
59 {64, 128, 128},
60 {128, 64, 128}};
61
62 thread_config_t large_batch_thread_configs[] = {
63 // Ordered by priority
64
65 // thread_k, thread_n, num_threads
66 {64, 256, 256},
67 {64, 128, 128},
68 {128, 64, 128}};
69
70 typedef struct {
71 int blocks_per_sm;
72 thread_config_t tb_cfg;
73 } exec_config_t;
74
75 int get_scales_cache_size(thread_config_t const& th_config, int prob_m,
76 int prob_n, int prob_k, int num_bits, int group_size,
77 int stages) {
78 int tb_n = th_config.thread_n;
79 int tb_k = th_config.thread_k;
80
81 // Get max scale groups per thread-block
82 int tb_groups;
83 if (group_size == -1) {
84 tb_groups = 1;
85 } else {
86 tb_groups = div_ceil(tb_k, group_size);
87 }
88
89 int tb_scales = tb_groups * tb_n * 2;
90 return tb_scales * stages;
91 }
92
93 int get_kernel_cache_size(thread_config_t const& th_config, bool m_block_size_8,
94 int thread_m_blocks, int prob_m, int prob_n,
95 int prob_k, int num_bits, int group_size, int has_zp,
96 int is_zp_float, bool is_a_8bit, int stages) {
97 int pack_factor = 32 / num_bits;
98
99 // Get B size
100 int tb_k = th_config.thread_k;
101 int tb_n = th_config.thread_n;
102 int tb_m = thread_m_blocks * 16;
103
104 // shm size for block_sorted_ids/rd_block_sorted_ids/block_topk_weights
105 // both of them requires tb_m * 4 bytes (tb_m * int32 or tb_m * float32)
106 int sh_block_meta_size = tb_m * 16;
107 int sh_a_size = stages * (tb_m * tb_k) * (is_a_8bit ? 1 : 2);
108 int sh_b_size = stages * (tb_k * tb_n / pack_factor) * 4;
109 int sh_red_size = tb_m * (tb_n + 8) * 2;
110 int sh_bias_size = tb_n * 2;
111 int tmp_size =
112 (sh_b_size > sh_red_size ? sh_red_size : sh_b_size) + sh_bias_size;
113 tmp_size = max(max(sh_b_size, sh_red_size), tmp_size);
114
115 int sh_s_size = get_scales_cache_size(th_config, prob_m, prob_n, prob_k,
116 num_bits, group_size, stages);
117 int sh_zp_size = 0;
118 if (has_zp) {
119 if (is_zp_float)
120 sh_zp_size = sh_s_size;
121 else if (num_bits == 4)
122 sh_zp_size = sh_s_size / 4;
123 else if (num_bits == 8)
124 sh_zp_size = sh_s_size / 2;
125 }
126
127 int total_size =
128 tmp_size + sh_a_size + sh_s_size + sh_zp_size + sh_block_meta_size;
129
130 return total_size;
131 }
132
133 bool is_valid_config(thread_config_t const& th_config, bool m_block_size_8,
134 int thread_m_blocks, int prob_m, int prob_n, int prob_k,
135 int num_bits, int group_size, int has_zp, int is_zp_float,
136 bool is_a_8bit, int stages, int max_shared_mem) {
137 // Sanity
138 if (th_config.thread_k == -1 || th_config.thread_n == -1 ||
139 th_config.num_threads == -1) {
140 return false;
141 }
142
143 // Verify K/N are divisible by thread K/N
144 if (prob_k % th_config.thread_k != 0 || prob_n % th_config.thread_n != 0) {
145 return false;
146 }
147
148 // Verify min for thread K/N
149 if (th_config.thread_n < min_thread_n || th_config.thread_k < min_thread_k) {
150 return false;
151 }
152
153 // num_threads must be at least 128 (= 4 warps)
154 if (th_config.num_threads < 128) {
155 return false;
156 }
157
158 // Check that pipeline fits into cache
159 int cache_size = get_kernel_cache_size(
160 th_config, m_block_size_8, thread_m_blocks, prob_m, prob_n, prob_k,
161 num_bits, group_size, has_zp, is_zp_float, is_a_8bit, stages);
162 return cache_size <= max_shared_mem;
163 }
164
165 MarlinFuncPtr get_marlin_kernel(const vllm::ScalarType a_type,
166 const vllm::ScalarType b_type,
167 const vllm::ScalarType c_type,
168 const vllm::ScalarType s_type,
169 int thread_m_blocks, int thread_n_blocks,
170 int thread_k_blocks, bool m_block_size_8,
171 bool has_zp, int group_blocks, int threads,
172 bool is_zp_float, int stages) {
173 int num_bits = b_type.size_bits();
174 auto kernel = MarlinDefault;
175
176 #include "kernel_selector.h"
177
178 return kernel;
179 }
180
181 exec_config_t determine_exec_config(
182 const vllm::ScalarType& a_type, const vllm::ScalarType& b_type,
183 const vllm::ScalarType& c_type, const vllm::ScalarType& s_type, int prob_m,
184 int prob_n, int prob_k, int num_experts, int top_k, int thread_m_blocks,
185 bool m_block_size_8, int num_bits, int group_size, bool has_zp,
186 bool is_zp_float, bool is_a_8bit, int stages, int max_shared_mem, int sms) {
187 exec_config_t exec_cfg = exec_config_t{1, thread_config_t{-1, -1, -1}};
188 thread_config_t* thread_configs = thread_m_blocks > 1
189 ? large_batch_thread_configs
190 : small_batch_thread_configs;
191 int thread_configs_size =
192 thread_m_blocks > 1
193 ? sizeof(large_batch_thread_configs) / sizeof(thread_config_t)
194 : sizeof(small_batch_thread_configs) / sizeof(thread_config_t);
195
196 int count = 0;
197 constexpr int device_max_reg_size = 255 * 1024;
198 for (int i = 0; i < thread_configs_size; i++) {
199 thread_config_t th_config = thread_configs[i];
200
201 if (!is_valid_config(th_config, m_block_size_8, thread_m_blocks, prob_m,
202 prob_n, prob_k, num_bits, group_size, has_zp,
203 is_zp_float, is_a_8bit, stages,
204 max_shared_mem - 512)) {
205 continue;
206 }
207
208 int cache_size = get_kernel_cache_size(
209 th_config, m_block_size_8, thread_m_blocks, prob_m, prob_n, prob_k,
210 num_bits, group_size, has_zp, is_zp_float, is_a_8bit, stages);
211
212 int group_blocks = group_size == -1 ? -1 : (group_size / 16);
213
214 auto kernel = get_marlin_kernel(
215 a_type, b_type, c_type, s_type, thread_m_blocks,
216 th_config.thread_n / 16, th_config.thread_k / 16, m_block_size_8,
217 has_zp, group_blocks, th_config.num_threads, is_zp_float, stages);
218
219 if (kernel == MarlinDefault) continue;
220
221 cudaFuncAttributes attr;
222 cudaFuncGetAttributes(&attr, kernel);
223 int reg_size = max(attr.numRegs, 1) * th_config.num_threads * 4;
224 int allow_count = min(device_max_reg_size / reg_size,
225 max_shared_mem / (cache_size + 1536));
226 if (thread_m_blocks == 1)
227 allow_count = max(min(allow_count, 4), 1);
228 else
229 allow_count = max(min(allow_count, 2), 1);
230
231 if (prob_n / th_config.thread_n * prob_m * top_k * 4 < sms * allow_count) {
232 allow_count =
233 max(prob_n / th_config.thread_n * prob_m * top_k * 4 / sms, 1);
234 }
235
236 if (allow_count > count) {
237 count = allow_count;
238 exec_cfg = {count, th_config};
239 };
240 }
241
242 return exec_cfg;
243 }
244
245 void marlin_mm(const void* A, const void* B, void* C, void* C_tmp, void* b_bias,
246 void* a_s, void* b_s, void* g_s, void* zp,
247 void* sorted_token_ids, void* expert_ids,
248 void* num_tokens_past_padded, void* topk_weights,
249 int moe_block_size, int num_experts, int top_k,
250 bool mul_topk_weights, int prob_m, int prob_n, int prob_k,
251 void* workspace, vllm::ScalarType const& a_type,
252 vllm::ScalarType const& b_type, vllm::ScalarType const& c_type,
253 vllm::ScalarType const& s_type, bool has_bias, bool has_zp,
254 int group_size, int dev, cudaStream_t stream, int thread_k,
255 int thread_n, int sms, int blocks_per_sm, bool use_atomic_add,
256 bool use_fp32_reduce, bool is_zp_float) {
257 int thread_m_blocks = div_ceil(moe_block_size, 16);
258 bool m_block_size_8 = moe_block_size == 8;
259 bool is_a_8bit = a_type.size_bits() == 8;
260
261 STD_TORCH_CHECK(prob_m > 0 && prob_n > 0 && prob_k > 0, "Invalid MNK = [",
262 prob_m, ", ", prob_n, ", ", prob_k, "]");
263
264 int group_blocks;
265 if (group_size == -1) {
266 group_blocks = -1;
267 } else {
268 group_blocks = group_size / 16;
269 STD_TORCH_CHECK(prob_k % group_blocks == 0, "prob_k = ", prob_k,
270 " is not divisible by group_blocks = ", group_blocks);
271 }
272
273 int num_bits = b_type.size_bits();
274 const int4* A_ptr = (const int4*)A;
275 const int4* B_ptr = (const int4*)B;
276 int4* C_ptr = (int4*)C;
277 int4* C_tmp_ptr = (int4*)C_tmp;
278 const int4* bias_ptr = (const int4*)b_bias;
279 const float* a_s_ptr = (const float*)a_s;
280 const int4* b_s_ptr = (const int4*)b_s;
281 const float* g_s_ptr = (const float*)g_s;
282 const int4* zp_ptr = (const int4*)zp;
283 const int32_t* sorted_token_ids_ptr = (const int32_t*)sorted_token_ids;
284 const int32_t* expert_ids_ptr = (const int32_t*)expert_ids;
285 const int32_t* num_tokens_past_padded_ptr =
286 (const int32_t*)num_tokens_past_padded;
287 const float* topk_weights_ptr = (const float*)topk_weights;
288 int* locks = (int*)workspace;
289
290 int max_shared_mem = 0;
291 cudaDeviceGetAttribute(&max_shared_mem,
292 cudaDevAttrMaxSharedMemoryPerBlockOptin, dev);
293 STD_TORCH_CHECK(max_shared_mem > 0);
294
295 int major_capability, minor_capability;
296 cudaDeviceGetAttribute(&major_capability, cudaDevAttrComputeCapabilityMajor,
297 dev);
298 cudaDeviceGetAttribute(&minor_capability, cudaDevAttrComputeCapabilityMinor,
299 dev);
300 STD_TORCH_CHECK(major_capability * 10 + minor_capability >= 75,
301 "marlin kernel only support Turing or newer GPUs.");
302 int stages = 4;
303 if (major_capability == 7 && minor_capability == 5) {
304 stages = 2;
305 STD_TORCH_CHECK(a_type == vllm::kFloat16 || a_type == vllm::kS8,
306 "Turing only support FP16 or INT8 activation.");
307 }
308 if (a_type == vllm::kFE4M3fn) {
309 STD_TORCH_CHECK(major_capability * 10 + minor_capability >= 89,
310 "FP8 only support Ada Lovelace or newer GPUs.");
311 STD_TORCH_CHECK(
312 major_capability * 10 + minor_capability == 89 ||
313 major_capability == 12,
314 "Marlin W4A8-FP8 only support SM89 or SM12x device (It is slower than "
315 "Marlin W4A16 on other devices).");
316 }
317
318 // Set thread config
319 exec_config_t exec_cfg;
320 thread_config_t thread_tfg;
321 if (thread_k != -1 && thread_n != -1) {
322 thread_tfg = thread_config_t{thread_k, thread_n, thread_k * thread_n / 64};
323 if (blocks_per_sm == -1) blocks_per_sm = 1;
324 exec_cfg = exec_config_t{blocks_per_sm, thread_tfg};
325 STD_TORCH_CHECK(prob_n % thread_n == 0, "prob_n = ", prob_n,
326 " is not divisible by thread_n = ", thread_n);
327 STD_TORCH_CHECK(prob_k % thread_k == 0, "prob_k = ", prob_k,
328 " is not divisible by thread_k = ", thread_k);
329 } else {
330 // Auto config
331 exec_cfg = determine_exec_config(
332 a_type, b_type, c_type, s_type, prob_m, prob_n, prob_k, num_experts,
333 top_k, thread_m_blocks, m_block_size_8, num_bits, group_size, has_zp,
334 is_zp_float, is_a_8bit, stages, max_shared_mem, sms);
335 thread_tfg = exec_cfg.tb_cfg;
336 }
337
338 int num_threads = thread_tfg.num_threads;
339 thread_k = thread_tfg.thread_k;
340 thread_n = thread_tfg.thread_n;
341 int blocks = sms * exec_cfg.blocks_per_sm;
342 if (exec_cfg.blocks_per_sm > 1)
343 max_shared_mem = max_shared_mem / exec_cfg.blocks_per_sm - 1024;
344
345 int thread_k_blocks = thread_k / 16;
346 int thread_n_blocks = thread_n / 16;
347
348 STD_TORCH_CHECK(
349 is_valid_config(thread_tfg, m_block_size_8, thread_m_blocks, prob_m,
350 prob_n, prob_k, num_bits, group_size, has_zp, is_zp_float,
351 is_a_8bit, stages, max_shared_mem),
352 "Invalid thread config: thread_m_blocks = ", thread_m_blocks,
353 ", thread_k = ", thread_tfg.thread_k,
354 ", thread_n = ", thread_tfg.thread_n,
355 ", num_threads = ", thread_tfg.num_threads, " for MKN = [", prob_m, ", ",
356 prob_k, ", ", prob_n, "] and num_bits = ", num_bits,
357 ", group_size = ", group_size, ", has_zp = ", has_zp,
358 ", is_zp_float = ", is_zp_float, ", max_shared_mem = ", max_shared_mem);
359
360 int sh_cache_size = get_kernel_cache_size(
361 thread_tfg, m_block_size_8, thread_m_blocks, prob_m, prob_n, prob_k,
362 num_bits, group_size, has_zp, is_zp_float, is_a_8bit, stages);
363
364 auto kernel =
365 get_marlin_kernel(a_type, b_type, c_type, s_type, thread_m_blocks,
366 thread_n_blocks, thread_k_blocks, m_block_size_8,
367 has_zp, group_blocks, num_threads, is_zp_float, stages);
368
369 if (kernel == MarlinDefault) {
370 STD_TORCH_CHECK(false, "Unsupported shapes: MNK = [", prob_m, ", ", prob_n,
371 ", ", prob_k, "]", ", group_size = ", group_size,
372 ", thread_m_blocks = ", thread_m_blocks,
373 ", thread_n_blocks = ", thread_n_blocks,
374 ", thread_k_blocks = ", thread_k_blocks,
375 ", num_bits = ", num_bits);
376 }
377
378 cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize,
379 max_shared_mem);
380 // avoid ">>>" being formatted to "> > >"
381 // clang-format off
382 kernel<<<blocks, num_threads, max_shared_mem, stream>>>(
383 A_ptr, B_ptr, C_ptr, C_tmp_ptr, bias_ptr, a_s_ptr, b_s_ptr, g_s_ptr, zp_ptr,
384 sorted_token_ids_ptr, expert_ids_ptr, num_tokens_past_padded_ptr,
385 topk_weights_ptr, top_k, mul_topk_weights, prob_m, prob_n, prob_k, locks,
386 has_bias, use_atomic_add, use_fp32_reduce);
387 // clang-format on
388 }
389
390 } // namespace MARLIN_NAMESPACE_NAME
391
392 torch::stable::Tensor moe_wna16_marlin_gemm(
393 torch::stable::Tensor& a, std::optional<torch::stable::Tensor> c_or_none,
394 torch::stable::Tensor& b_q_weight,
395 std::optional<torch::stable::Tensor> const& b_bias_or_none,
396 torch::stable::Tensor& b_scales,
397 std::optional<torch::stable::Tensor> const& a_scales_or_none,
398 std::optional<torch::stable::Tensor> const& global_scale_or_none,
399 std::optional<torch::stable::Tensor> const& b_zeros_or_none,
400 torch::stable::Tensor& workspace, torch::stable::Tensor& sorted_token_ids,
401 torch::stable::Tensor& expert_ids,
402 torch::stable::Tensor& num_tokens_past_padded,
403 torch::stable::Tensor& topk_weights, int64_t moe_block_size, int64_t top_k,
404 bool mul_topk_weights, vllm::ScalarTypeId const& b_type_id, int64_t size_m,
405 int64_t size_n, int64_t size_k, bool use_atomic_add, bool use_fp32_reduce,
406 bool is_zp_float, int64_t thread_k, int64_t thread_n,
407 int64_t blocks_per_sm) {
408 vllm::ScalarTypeId a_type_id, c_type_id, s_type_id;
409
410 auto c_dtype = a.scalar_type();
411 if (a.scalar_type() == torch::headeronly::ScalarType::Half) {
412 a_type_id = vllm::kFloat16.id();
413 c_type_id = vllm::kFloat16.id();
414 } else if (a.scalar_type() == torch::headeronly::ScalarType::BFloat16) {
415 a_type_id = vllm::kBFloat16.id();
416 c_type_id = vllm::kBFloat16.id();
417 } else {
418 c_dtype = b_scales.scalar_type();
419 if (b_scales.scalar_type() == torch::headeronly::ScalarType::Half) {
420 c_type_id = vllm::kFloat16.id();
421 } else if (b_scales.scalar_type() ==
422 torch::headeronly::ScalarType::BFloat16) {
423 c_type_id = vllm::kBFloat16.id();
424 } else {
425 c_type_id = vllm::kBFloat16.id();
426
427 STD_TORCH_CHECK(c_or_none.has_value(), "c must be passed for W4A8-FP4");
428 torch::stable::Tensor c = c_or_none.value();
429 c_dtype = c.scalar_type();
430
431 if (c.scalar_type() == torch::headeronly::ScalarType::Half) {
432 c_type_id = vllm::kFloat16.id();
433 } else if (c.scalar_type() == torch::headeronly::ScalarType::BFloat16) {
434 c_type_id = vllm::kBFloat16.id();
435 } else {
436 STD_TORCH_CHECK(false, "unsupported c dtype");
437 }
438 }
439
440 if (a.scalar_type() == torch::headeronly::ScalarType::Float8_e4m3fn) {
441 a_type_id = vllm::kFE4M3fn.id();
442 } else if (a.scalar_type() == torch::headeronly::ScalarType::Char) {
443 a_type_id = vllm::kS8.id();
444 } else {
445 STD_TORCH_CHECK(false, "unsupported `a` scalar_type");
446 }
447 }
448
449 s_type_id = c_type_id;
450 if (b_type_id == vllm::kFE2M1f.id()) {
451 if (b_scales.scalar_type() ==
452 torch::headeronly::ScalarType::Float8_e4m3fn) {
453 s_type_id = vllm::kFE4M3fn.id();
454 } else if (b_scales.scalar_type() ==
455 torch::headeronly::ScalarType::Float8_e8m0fnu) {
456 s_type_id = vllm::kFE8M0fnu.id();
457 } else {
458 STD_TORCH_CHECK(
459 false, "When b_type = float4_e2m1f, b_scale scalar type must be",
460 "float8_e4m3fn (for NVFP4) or float8_e8m0fnu (for MXFP4).");
461 }
462 } else if (b_type_id == vllm::kFE4M3fn.id() &&
463 b_scales.scalar_type() ==
464 torch::headeronly::ScalarType::Float8_e8m0fnu) {
465 s_type_id = vllm::kFE8M0fnu.id();
466 }
467
468 vllm::ScalarType a_type = vllm::ScalarType::from_id(a_type_id);
469 vllm::ScalarType b_type = vllm::ScalarType::from_id(b_type_id);
470 vllm::ScalarType c_type = vllm::ScalarType::from_id(c_type_id);
471 vllm::ScalarType s_type = vllm::ScalarType::from_id(s_type_id);
472
473 int pack_factor = 32 / b_type.size_bits();
474 int num_experts = b_q_weight.size(0);
475
476 if (moe_block_size != 8) {
477 STD_TORCH_CHECK(moe_block_size % 16 == 0,
478 "unsupported moe_block_size=", moe_block_size);
479 STD_TORCH_CHECK(moe_block_size >= 16 && moe_block_size <= 64,
480 "unsupported moe_block_size=", moe_block_size);
481 }
482
483 // Verify A
484 STD_TORCH_CHECK(a.size(0) == size_m,
485 "Shape mismatch: a.size(0) = ", a.size(0),
486 ", size_m = ", size_m);
487 STD_TORCH_CHECK(a.size(1) == size_k,
488 "Shape mismatch: a.size(1) = ", a.size(1),
489 ", size_k = ", size_k);
490
491 // Verify B
492 STD_TORCH_CHECK(
493 size_k % MARLIN_NAMESPACE_NAME::tile_size == 0, "size_k = ", size_k,
494 " is not divisible by tile_size = ", MARLIN_NAMESPACE_NAME::tile_size);
495 STD_TORCH_CHECK(
496 (size_k / MARLIN_NAMESPACE_NAME::tile_size) == b_q_weight.size(1),
497 "Shape mismatch: b_q_weight.size(1) = ", b_q_weight.size(1),
498 ", size_k = ", size_k,
499 ", tile_size = ", MARLIN_NAMESPACE_NAME::tile_size);
500 STD_TORCH_CHECK(
501 b_q_weight.size(2) % MARLIN_NAMESPACE_NAME::tile_size == 0,
502 "b_q_weight.size(2) = ", b_q_weight.size(2),
503 " is not divisible by tile_size = ", MARLIN_NAMESPACE_NAME::tile_size);
504 int actual_size_n =
505 (b_q_weight.size(2) / MARLIN_NAMESPACE_NAME::tile_size) * pack_factor;
506 STD_TORCH_CHECK(size_n == actual_size_n, "size_n = ", size_n,
507 ", actual_size_n = ", actual_size_n);
508
509 // Verify device and strides
510 STD_TORCH_CHECK(a.device().is_cuda(), "A is not on GPU");
511 STD_TORCH_CHECK(a.is_contiguous(), "A is not contiguous");
512
513 STD_TORCH_CHECK(b_q_weight.device().is_cuda(), "b_q_weight is not on GPU");
514 STD_TORCH_CHECK(b_q_weight.is_contiguous(), "b_q_weight is not contiguous");
515
516 STD_TORCH_CHECK(b_scales.device().is_cuda(), "b_scales is not on GPU");
517 STD_TORCH_CHECK(b_scales.is_contiguous(), "b_scales is not contiguous");
518
519 torch::stable::Tensor a_scales;
520 constexpr auto kFloat = torch::headeronly::ScalarType::Float;
521
522 if (a_scales_or_none.has_value()) {
523 a_scales = a_scales_or_none.value();
524 STD_TORCH_CHECK(a_type.size_bits() == 8,
525 "a_scales can only be used for 8bit activation.");
526 } else {
527 a_scales = torch::stable::new_empty(a, {0}, kFloat);
528 STD_TORCH_CHECK(
529 a_type.size_bits() != 8,
530 "the a_scales parameter must be passed for 8bit activation.");
531 }
532
533 // sms: number of SMs to use for the kernel
534 int sms = -1;
535 cudaDeviceGetAttribute(&sms, cudaDevAttrMultiProcessorCount, a.get_device());
536
537 // Alloc buffers
538 torch::stable::accelerator::DeviceGuard device_guard(a.get_device_index());
539 torch::stable::Tensor c;
540 if (c_or_none.has_value()) {
541 c = c_or_none.value();
542 STD_TORCH_CHECK(c.device().is_cuda(), "c is not on GPU");
543 STD_TORCH_CHECK(c.is_contiguous(), "c is not contiguous");
544 STD_TORCH_CHECK(c.size(0) == size_m * top_k,
545 "Shape mismatch: c.size(0) = ", c.size(0),
546 ", size_m * topk = ", size_m * top_k);
547 STD_TORCH_CHECK(c.size(1) == size_n,
548 "Shape mismatch: c.size(1) = ", c.size(1),
549 ", size_n = ", size_n);
550 } else {
551 c = torch::stable::new_empty(a, {size_m * top_k, size_n}, c_dtype);
552 }
553
554 // Alloc C tmp buffer that is going to be used for the global reduce
555 torch::stable::Tensor c_tmp;
556 if (use_fp32_reduce && !use_atomic_add) {
557 // max num of threadblocks is sms * 4
558 long max_c_tmp_size = min(
559 (long)size_n * sorted_token_ids.size(0),
560 (long)sms * 4 * moe_block_size * MARLIN_NAMESPACE_NAME::max_thread_n);
561 if (moe_block_size == 8) max_c_tmp_size *= 2;
562 c_tmp = torch::stable::new_empty(a, {max_c_tmp_size}, kFloat);
563 } else {
564 c_tmp = torch::stable::new_empty(a, {0}, kFloat);
565 }
566
567 // Detect group size.
568 int num_groups = -1;
569 int group_size = -1;
570
571 int rank = b_scales.dim();
572 STD_TORCH_CHECK(rank == 3, "b_scales rank = ", rank, " is not 3");
573 STD_TORCH_CHECK(b_scales.size(2) == size_n,
574 "b_scales dim 2 = ", b_scales.size(2),
575 " is not size_n = ", size_n);
576 num_groups = b_scales.size(1);
577
578 if (num_groups > 1) {
579 STD_TORCH_CHECK(
580 size_k % num_groups == 0, "size_k = ", size_k,
581 ", is not divisible by b_scales.size(1) = ", b_scales.size(1));
582 group_size = size_k / num_groups;
583 } else {
584 group_size = -1;
585 }
586
587 torch::stable::Tensor global_scale;
588 if (global_scale_or_none.has_value()) {
589 global_scale = global_scale_or_none.value();
590 STD_TORCH_CHECK(b_type == vllm::kFE2M1f && s_type == vllm::kFE4M3fn,
591 "global_scale can only be used for nvfp4 format.");
592 } else {
593 global_scale = torch::stable::new_empty(a, {0}, kFloat);
594 STD_TORCH_CHECK(
595 !(b_type == vllm::kFE2M1f && s_type == vllm::kFE4M3fn),
596 "the global_scale parameter must be passed for nvfp4 format.");
597 }
598
599 bool has_bias = b_bias_or_none.has_value();
600 torch::stable::Tensor b_bias;
601 if (has_bias) {
602 b_bias = b_bias_or_none.value();
603 STD_TORCH_CHECK(b_bias.device().is_cuda(), "b_bias is not on GPU");
604 STD_TORCH_CHECK(b_bias.is_contiguous(), "b_bias is not contiguous");
605 STD_TORCH_CHECK(b_bias.size(1) == size_n, "b_bias.size(1) != size_n");
606 STD_TORCH_CHECK(b_bias.stride(1) == 1, "b_bias.stride(1) != 1");
607 } else {
608 b_bias = torch::stable::new_empty(a, {0}, c_dtype);
609 }
610
611 torch::stable::Tensor b_zeros;
612 if (b_zeros_or_none.has_value()) {
613 b_zeros = b_zeros_or_none.value();
614 STD_TORCH_CHECK(b_zeros.device().is_cuda(), "b_zeros is not on GPU");
615 STD_TORCH_CHECK(b_zeros.is_contiguous(), "b_zeros is not contiguous");
616 } else {
617 b_zeros = torch::stable::new_empty(a, {0}, c_dtype);
618 }
619 bool has_zp = b_zeros.size(-1) > 0;
620 if (has_zp) {
621 STD_TORCH_CHECK(
622 b_type == vllm::kU4 || b_type == vllm::kU8,
623 "b_type must be u4 or u8 when has_zp = True. Got = ", b_type.str());
624 } else {
625 STD_TORCH_CHECK(b_type == vllm::kU4B8 || b_type == vllm::kU8B128 ||
626 b_type == vllm::kS4 || b_type == vllm::kS8 ||
627 b_type == vllm::kFE4M3fn || b_type == vllm::kFE2M1f,
628 "b_type must be uint4b8, uint8b128, int4, int8, "
629 "float8_e4m3fn or float4_e2m1f when has_zp = False. Got = ",
630 b_type.str());
631 }
632
633 if (has_zp && is_zp_float) {
634 STD_TORCH_CHECK(
635 a.scalar_type() == torch::headeronly::ScalarType::Half,
636 "Computation type must be float16 (half) when using float zero "
637 "points.");
638 }
639
640 // Verify b_zeros
641 if (has_zp) {
642 int rank = b_zeros.dim();
643 STD_TORCH_CHECK(rank == 3, "b_zeros rank = ", rank, " is not 3");
644 if (is_zp_float) {
645 STD_TORCH_CHECK(b_zeros.size(2) == size_n,
646 "b_zeros dim 2 = ", b_zeros.size(2),
647 " is not size_n = ", size_n);
648 STD_TORCH_CHECK(num_groups == b_zeros.size(1),
649 "b_zeros dim 1 = ", b_zeros.size(1),
650 " is not num_groups = ", num_groups);
651 STD_TORCH_CHECK(num_groups != -1, "num_groups must be != -1");
652 } else {
653 STD_TORCH_CHECK(b_zeros.size(1) == num_groups,
654 "b_zeros dim 1 = ", b_zeros.size(1),
655 " is not num_groups = ", num_groups);
656 STD_TORCH_CHECK(b_zeros.size(2) == size_n / pack_factor,
657 "b_zeros dim 2 = ", b_zeros.size(2),
658 " is not size_n / pack_factor = ", size_n / pack_factor);
659 }
660 }
661
662 // Verify workspace size
663 STD_TORCH_CHECK(size_n % MARLIN_NAMESPACE_NAME::min_thread_n == 0,
664 "size_n = ", size_n, ", is not divisible by min_thread_n = ",
665 MARLIN_NAMESPACE_NAME::min_thread_n);
666
667 int max_n_tiles = size_n / MARLIN_NAMESPACE_NAME::min_thread_n;
668 int min_workspace_size = min(
669 max_n_tiles * (int)(sorted_token_ids.size(0) / moe_block_size), sms * 4);
670 STD_TORCH_CHECK(workspace.numel() >= min_workspace_size,
671 "workspace.numel = ", workspace.numel(),
672 " is below min_workspace_size = ", min_workspace_size);
673
674 int dev = a.get_device();
675
676 STD_TORCH_CHECK(
677 a_scales.scalar_type() == torch::headeronly::ScalarType::Float,
678 "scalar type of a_scales must be float");
679 STD_TORCH_CHECK(
680 global_scale.scalar_type() == torch::headeronly::ScalarType::Float,
681 "scalar type of global_scale must be float");
682 if (a_type.size_bits() == 16) {
683 STD_TORCH_CHECK(
684 a.scalar_type() == c.scalar_type(),
685 "scalar type of a must be the same with c for 16 bit activation");
686 }
687
688 MARLIN_NAMESPACE_NAME::marlin_mm(
689 a.const_data_ptr(), b_q_weight.const_data_ptr(), c.mutable_data_ptr(),
690 c_tmp.mutable_data_ptr(), b_bias.mutable_data_ptr(),
691 a_scales.mutable_data_ptr(), b_scales.mutable_data_ptr(),
692 global_scale.mutable_data_ptr(), b_zeros.mutable_data_ptr(),
693 sorted_token_ids.mutable_data_ptr(), expert_ids.mutable_data_ptr(),
694 num_tokens_past_padded.mutable_data_ptr(),
695 topk_weights.mutable_data_ptr(), moe_block_size, num_experts, top_k,
696 mul_topk_weights, size_m, size_n, size_k, workspace.mutable_data_ptr(),
697 a_type, b_type, c_type, s_type, has_bias, has_zp, group_size, dev,
698 get_current_cuda_stream(dev), thread_k, thread_n, sms, blocks_per_sm,
699 use_atomic_add, use_fp32_reduce, is_zp_float);
700
701 return c;
702 }
703
704 STABLE_TORCH_LIBRARY_IMPL(_moe_C, CUDA, m) {
705 m.impl("moe_wna16_marlin_gemm", TORCH_BOX(&moe_wna16_marlin_gemm));
706 }