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