main
py 315 lines 10.5 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 import glob
4 import itertools
5 import os
6 import subprocess
7 import sys
8
9 import jinja2
10
11 ARCHS = []
12 SUPPORT_FP8 = False
13 SUPPORT_SM75 = False
14 SUPPORT_SM80 = False
15 for arch in sys.argv[1].split(","):
16 arch = arch[: arch.index(".") + 2].replace(".", "")
17 arch = int(arch)
18 # SM89 and the SM12x family (SM120 RTX 5090, SM121 DGX Spark GB10)
19 # fully support mma.sync.aligned.m16n8k32.row.col.f32.e4m3.e4m3.f32.
20 # SM90 and SM100 can use this PTX, but it’s simulated
21 # with FP16 MMA, so it cannot achieve any acceleration.
22 if arch == 89 or arch // 10 == 12:
23 SUPPORT_FP8 = True
24 if arch >= 80:
25 SUPPORT_SM80 = True
26 if arch == 75:
27 SUPPORT_SM75 = True
28
29 FILE_HEAD_COMMENT = """
30 // auto generated by generate_kernels.py
31 // clang-format off
32 """.lstrip()
33
34 FILE_HEAD = (
35 FILE_HEAD_COMMENT
36 + """
37 #include "kernel.h"
38 #include "marlin_template.h"
39
40 namespace MARLIN_NAMESPACE_NAME {
41 """
42 )
43
44 TEMPLATE = (
45 "template __global__ void Marlin<"
46 "{{a_type_id}}, "
47 "{{b_type_id}}, "
48 "{{c_type_id}}, "
49 "{{s_type_id}}, "
50 "{{threads}}, "
51 "{{thread_m_blocks}}, "
52 "{{thread_n_blocks}}, "
53 "{{thread_k_blocks}}, "
54 "{{m_block_size_8}}, "
55 "{{stages}}, "
56 "{{group_blocks}}, "
57 "{{is_zp_float}}>"
58 "( MARLIN_KERNEL_PARAMS );"
59 )
60
61 THREAD_CONFIGS = [(128, 128, 256), (64, 256, 256), (64, 128, 128), (128, 64, 128)]
62
63 THREAD_M_BLOCKS = [0.5, 1, 2, 3, 4]
64
65 QUANT_CONFIGS = [
66 # AWQ-INT4
67 {
68 "b_type": "kU4",
69 "thread_configs": THREAD_CONFIGS,
70 "thread_m_blocks": THREAD_M_BLOCKS,
71 "group_blocks": [-1, 2, 4, 8],
72 },
73 # GPTQ-INT4
74 {
75 "b_type": "kU4B8",
76 "thread_configs": THREAD_CONFIGS,
77 "thread_m_blocks": THREAD_M_BLOCKS,
78 "group_blocks": [-1, 2, 4, 8],
79 },
80 # AWQ-INT8
81 {
82 "b_type": "kU8B128",
83 "thread_configs": THREAD_CONFIGS,
84 "thread_m_blocks": THREAD_M_BLOCKS,
85 "group_blocks": [-1, 2, 4, 8],
86 },
87 # FP8
88 {
89 "b_type": "kFE4M3fn",
90 "thread_configs": THREAD_CONFIGS,
91 "thread_m_blocks": THREAD_M_BLOCKS,
92 "group_blocks": [-1, 8],
93 },
94 # NVFP4
95 {
96 "b_type": "kFE2M1f",
97 "s_type": "kFE4M3fn",
98 "thread_configs": THREAD_CONFIGS,
99 "thread_m_blocks": THREAD_M_BLOCKS,
100 "group_blocks": [1],
101 },
102 # MXFP4
103 {
104 "a_type": ["kBFloat16"],
105 "b_type": "kFE2M1f",
106 "s_type": "kFE8M0fnu",
107 "thread_configs": THREAD_CONFIGS,
108 "thread_m_blocks": THREAD_M_BLOCKS,
109 "group_blocks": [2],
110 },
111 # MXFP8
112 {
113 "a_type": ["kBFloat16"],
114 "b_type": "kFE4M3fn",
115 "s_type": "kFE8M0fnu",
116 "thread_configs": THREAD_CONFIGS,
117 "thread_m_blocks": THREAD_M_BLOCKS,
118 "group_blocks": [2],
119 },
120 # AWQ-INT4 with INT8 activation
121 {
122 "a_type": ["kS8"],
123 "b_type": "kU4",
124 "thread_configs": THREAD_CONFIGS,
125 "thread_m_blocks": [1, 2, 3, 4],
126 "group_blocks": [-1, 2, 4, 8],
127 },
128 # GPTQ-INT4 with INT8 activation
129 {
130 "a_type": ["kS8"],
131 "b_type": "kU4B8",
132 "thread_configs": THREAD_CONFIGS,
133 "thread_m_blocks": [1, 2, 3, 4],
134 "group_blocks": [-1, 2, 4, 8],
135 },
136 # GPTQ-INT4 with FP8 activation
137 {
138 "a_type": ["kFE4M3fn"],
139 "b_type": "kU4B8",
140 "thread_configs": THREAD_CONFIGS,
141 "thread_m_blocks": [1, 2, 3, 4],
142 "group_blocks": [-1, 2, 4, 8],
143 },
144 # AWQ-INT4 with FP8 activation
145 {
146 "a_type": ["kFE4M3fn"],
147 "b_type": "kU4",
148 "thread_configs": THREAD_CONFIGS,
149 "thread_m_blocks": [1, 2, 3, 4],
150 "group_blocks": [-1, 2, 4, 8],
151 },
152 # MXFP4 with FP8 activation
153 {
154 "a_type": ["kFE4M3fn"],
155 "b_type": "kFE2M1f",
156 "c_type": ["kBFloat16"],
157 "s_type": "kFE8M0fnu",
158 "thread_configs": THREAD_CONFIGS,
159 "thread_m_blocks": [1, 2, 3, 4],
160 "group_blocks": [2],
161 },
162 ]
163
164
165 def remove_old_kernels():
166 for filename in glob.glob(os.path.dirname(__file__) + "/*kernel_*.cu"):
167 subprocess.call(["rm", "-f", filename])
168
169 filename = os.path.dirname(__file__) + "/kernel_selector.h"
170 subprocess.call(["rm", "-f", filename])
171
172
173 def generate_new_kernels():
174 result_dict = {}
175 sm_75_result_dict = {}
176
177 for quant_config in QUANT_CONFIGS:
178 c_types = quant_config.get("c_type", ["kFloat16", "kBFloat16"])
179 a_types = quant_config.get("a_type", ["kFloat16", "kBFloat16"])
180 b_type = quant_config["b_type"]
181 all_group_blocks = quant_config["group_blocks"]
182 all_m_blocks = quant_config["thread_m_blocks"]
183 all_thread_configs = quant_config["thread_configs"]
184
185 for a_type, c_type in itertools.product(a_types, c_types):
186 if not SUPPORT_FP8 and a_type == "kFE4M3fn":
187 continue
188 if "16" in a_type and "16" in c_type and a_type != c_type:
189 continue
190 s_type = quant_config.get("s_type", c_type)
191 if (a_type, b_type, c_type) not in result_dict:
192 result_dict[(a_type, b_type, c_type)] = []
193 if a_type in ["kFloat16", "kS8"] and c_type == "kFloat16":
194 sm_75_result_dict[(a_type, b_type, c_type)] = []
195
196 for group_blocks, m_blocks, thread_configs in itertools.product(
197 all_group_blocks, all_m_blocks, all_thread_configs
198 ):
199 thread_k, thread_n, threads = thread_configs
200
201 if threads == 256:
202 # for small batch (m_blocks == 1),
203 # we only need (128, 128, 256)
204 # for large batch (m_blocks > 1),
205 # we only need (64, 256, 256)
206 if m_blocks <= 1 and (thread_k, thread_n) != (128, 128):
207 continue
208 if m_blocks > 1 and (thread_k, thread_n) != (64, 256):
209 continue
210
211 config = {
212 "threads": threads,
213 "s_type": s_type,
214 "thread_m_blocks": max(m_blocks, 1),
215 "thread_k_blocks": thread_k // 16,
216 "thread_n_blocks": thread_n // 16,
217 "m_block_size_8": "true" if m_blocks == 0.5 else "false",
218 "stages": 4,
219 "group_blocks": group_blocks,
220 "is_zp_float": "false",
221 }
222
223 if SUPPORT_SM80:
224 result_dict[(a_type, b_type, c_type)].append(config)
225 if (a_type, b_type, c_type) in sm_75_result_dict and SUPPORT_SM75:
226 config_sm75 = config.copy()
227 config_sm75["stages"] = 2
228 sm_75_result_dict[(a_type, b_type, c_type)].append(config_sm75)
229
230 kernel_selector_str = FILE_HEAD_COMMENT
231
232 for result_dict_tmp in [result_dict, sm_75_result_dict]:
233 for (a_type, b_type, c_type), config_list in result_dict_tmp.items():
234 all_template_str_list = []
235 if not config_list:
236 continue
237 for config in config_list:
238 s_type = config["s_type"]
239 template_str = jinja2.Template(TEMPLATE).render(
240 a_type_id=f"vllm::{a_type}.id()",
241 b_type_id=f"vllm::{b_type}.id()",
242 c_type_id=f"vllm::{c_type}.id()",
243 s_type_id=f"vllm::{s_type}.id()",
244 **config,
245 )
246 all_template_str_list.append(template_str)
247
248 conditions = [
249 f"a_type == vllm::{a_type}",
250 f"b_type == vllm::{b_type}",
251 f"c_type == vllm::{c_type}",
252 f"s_type == vllm::{s_type}",
253 f"threads == {config['threads']}",
254 f"thread_m_blocks == {config['thread_m_blocks']}",
255 f"thread_n_blocks == {config['thread_n_blocks']}",
256 f"thread_k_blocks == {config['thread_k_blocks']}",
257 f"m_block_size_8 == {config['m_block_size_8']}",
258 f"stages == {config['stages']}",
259 f"group_blocks == {config['group_blocks']}",
260 f"is_zp_float == {config['is_zp_float']}",
261 ]
262 conditions = " && ".join(conditions)
263
264 if kernel_selector_str == FILE_HEAD_COMMENT:
265 kernel_selector_str += f"if ({conditions})\n kernel = "
266 else:
267 kernel_selector_str += f"else if ({conditions})\n kernel = "
268
269 kernel_template2 = (
270 "Marlin<{{a_type_id}}, {{b_type_id}}, {{c_type_id}}, "
271 "{{s_type_id}}, {{threads}}, {{thread_m_blocks}}, "
272 "{{thread_n_blocks}}, {{thread_k_blocks}}, "
273 "{{m_block_size_8}}, {{stages}}, {{group_blocks}}, "
274 "{{is_zp_float}}>;"
275 )
276
277 kernel_selector_str += (
278 jinja2.Template(kernel_template2).render(
279 a_type_id=f"vllm::{a_type}.id()",
280 b_type_id=f"vllm::{b_type}.id()",
281 c_type_id=f"vllm::{c_type}.id()",
282 s_type_id=f"vllm::{s_type}.id()",
283 **config,
284 )
285 + "\n"
286 )
287
288 file_content = FILE_HEAD + "\n\n"
289 file_content += "\n\n".join(all_template_str_list) + "\n\n}\n"
290 if a_type == "kFE4M3fn":
291 filename = f"sm89_kernel_{a_type[1:]}_{b_type[1:]}_{c_type[1:]}.cu"
292 elif result_dict_tmp is sm_75_result_dict:
293 filename = f"sm75_kernel_{a_type[1:]}_{b_type[1:]}_{c_type[1:]}.cu"
294 else:
295 filename = f"sm80_kernel_{a_type[1:]}_{b_type[1:]}_{c_type[1:]}.cu"
296
297 filename = filename.lower()
298
299 with open(os.path.join(os.path.dirname(__file__), filename), "w") as f:
300 f.write(file_content)
301
302 if not SUPPORT_FP8 and kernel_selector_str != FILE_HEAD_COMMENT:
303 kernel_selector_str += (
304 "else if (a_type == vllm::kFE4M3fn)\n"
305 " STD_TORCH_CHECK(false, "
306 '"marlin kernel with fp8 activation is not built.");'
307 )
308
309 with open(os.path.join(os.path.dirname(__file__), "kernel_selector.h"), "w") as f:
310 f.write(kernel_selector_str)
311
312
313 if __name__ == "__main__":
314 remove_old_kernels()
315 generate_new_kernels()