A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 15, 21:00 UTC
[core] moe fp8 block quant tuning support (#14068)
Signed-off-by: Divakar Verma <divakar.verma@amd.com>
Divakar Verma committed
Mar 3, 2025 at 19:30 UTC
bb5b640359cc6695cb7818a24680e226f72a4da7
2 files changed
+129
-57
benchmarks/kernels/benchmark_moe.py
+67
-31
@@ -40,6 +40,7 @@ def benchmark_config(
40
use_fp8_w8a8: bool,
41
use_int8_w8a16: bool,
42
num_iters: int = 100,
43
+ block_quant_shape: List[int] = None,
44
) -> float:
45
init_dtype = torch.float16 if use_fp8_w8a8 else dtype
46
x = torch.randn(num_tokens, hidden_size, dtype=dtype)
@@ -81,8 +82,24 @@ def benchmark_config(
82
dtype=torch.float32)
83
w2_scale = torch.randn((hidden_size, num_experts), dtype=torch.float32)
84
if use_fp8_w8a8:
84
- w1_scale = torch.randn(num_experts, dtype=torch.float32)
85
- w2_scale = torch.randn(num_experts, dtype=torch.float32)
85
+ if block_quant_shape:
86
+ block_n, block_k = block_quant_shape[0], block_quant_shape[1]
87
+ E = num_experts
88
+ N = shard_intermediate_size // 2
89
+ K = hidden_size
90
+ factor_for_scale = 1e-2
91
+ n_tiles_w1 = (2 * N + block_n - 1) // block_n
92
+ n_tiles_w2 = (K + block_n - 1) // block_n
93
+ k_tiles_w1 = (K + block_k - 1) // block_k
94
+ k_tiles_w2 = (N + block_k - 1) // block_k
95
+ w1_scale = torch.rand((E, n_tiles_w1, k_tiles_w1),
96
+ dtype=torch.float32) * factor_for_scale
97
+ w2_scale = torch.rand((E, n_tiles_w2, k_tiles_w2),
98
+ dtype=torch.float32) * factor_for_scale
99
+ else:
100
+ w1_scale = torch.randn(num_experts, dtype=torch.float32)
101
+ w2_scale = torch.randn(num_experts, dtype=torch.float32)
102
+
103
a1_scale = torch.randn(1, dtype=torch.float32)
104
a2_scale = torch.randn(1, dtype=torch.float32)
105
@@ -111,6 +128,7 @@ def benchmark_config(
128
w2_scale=w2_scale,
129
a1_scale=a1_scale,
130
a2_scale=a2_scale,
131
+ block_shape=block_quant_shape,
132
)
133
134
# JIT compilation & warmup
@@ -175,7 +193,8 @@ def get_rocm_tuning_space(use_fp16):
193
return param_ranges
194
195
178
-def get_configs_compute_bound(use_fp16) -> list[dict[str, int]]:
196
+def get_configs_compute_bound(use_fp16,
197
+ block_quant_shape) -> list[dict[str, int]]:
198
configs: list[BenchmarkConfig] = []
199
200
if current_platform.is_rocm():
@@ -204,17 +223,27 @@ def get_configs_compute_bound(use_fp16) -> list[dict[str, int]]:
223
for config_values in product(*values):
224
config = dict(zip(keys, config_values))
225
configs.append(config)
226
+
227
+ # Remove configs that are not compatible with fp8 block quantization
228
+ # BLOCK_SIZE_K must be a multiple of block_k
229
+ # BLOCK_SIZE_N must be a multiple of block_n
230
+ if block_quant_shape is not None and not use_fp16:
231
+ block_n, block_k = block_quant_shape[0], block_quant_shape[1]
232
+ for config in configs[:]:
233
+ if config["BLOCK_SIZE_K"] % block_k != 0 or config[
234
+ "BLOCK_SIZE_N"] % block_n != 0:
235
+ configs.remove(config)
236
return configs
237
238
239
def prune_rocm_search_space(num_tokens, shard_intermediate_size, hidden_size,
211
- search_space, is_fp16):
240
+ search_space, is_fp16, topk):
241
N1, K1 = shard_intermediate_size, hidden_size
242
N2, K2 = hidden_size, shard_intermediate_size // 2
214
- pruned_space_1 = prune_rocm_configs(num_tokens * 2, N1, K1, search_space,
215
- is_fp16)
216
- pruned_space_2 = prune_rocm_configs(num_tokens * 2, N2, K2, search_space,
217
- is_fp16)
243
+ pruned_space_1 = prune_rocm_configs(num_tokens * topk, N1, K1,
244
+ search_space, is_fp16)
245
+ pruned_space_2 = prune_rocm_configs(num_tokens * topk, N2, K2,
246
+ search_space, is_fp16)
247
search_space = merge_unique_dicts(pruned_space_1, pruned_space_2)
248
return search_space
249
@@ -372,6 +401,7 @@ class BenchmarkWorker:
401
use_fp8_w8a8: bool,
402
use_int8_w8a16: bool,
403
search_space: list[dict[str, int]],
404
+ block_quant_shape: list[int],
405
) -> dict[str, int]:
406
best_config = None
407
best_time = float("inf")
@@ -380,21 +410,23 @@ class BenchmarkWorker:
410
search_space = prune_rocm_search_space(num_tokens,
411
shard_intermediate_size,
412
hidden_size, search_space,
383
- is_fp16)
413
+ is_fp16, topk)
414
415
with torch.cuda.device(self.device_id):
416
for config in tqdm(search_space):
417
try:
388
- kernel_time = benchmark_config(config,
389
- num_tokens,
390
- num_experts,
391
- shard_intermediate_size,
392
- hidden_size,
393
- topk,
394
- dtype,
395
- use_fp8_w8a8,
396
- use_int8_w8a16,
397
- num_iters=20)
418
+ kernel_time = benchmark_config(
419
+ config,
420
+ num_tokens,
421
+ num_experts,
422
+ shard_intermediate_size,
423
+ hidden_size,
424
+ topk,
425
+ dtype,
426
+ use_fp8_w8a8,
427
+ use_int8_w8a16,
428
+ num_iters=20,
429
+ block_quant_shape=block_quant_shape)
430
except triton.runtime.autotuner.OutOfResources:
431
# Some configurations may be invalid and fail to compile.
432
continue
@@ -436,8 +468,8 @@ def sort_config(config: BenchmarkConfig) -> BenchmarkConfig:
468
469
def save_configs(configs: dict[int, BenchmarkConfig], num_experts: int,
470
shard_intermediate_size: int, hidden_size: int, topk: int,
439
- dtype: torch.dtype, use_fp8_w8a8: bool,
440
- use_int8_w8a16: bool) -> None:
471
+ dtype: torch.dtype, use_fp8_w8a8: bool, use_int8_w8a16: bool,
472
+ block_quant_shape: List[int]) -> None:
473
dtype_str = get_config_dtype_str(dtype,
474
use_int8_w8a16=use_int8_w8a16,
475
use_fp8_w8a8=use_fp8_w8a8)
@@ -445,7 +477,7 @@ def save_configs(configs: dict[int, BenchmarkConfig], num_experts: int,
477
# NOTE(woosuk): The current naming convention uses w2.shape[2], which
478
# is the intermediate size after silu_and_mul.
479
filename = get_config_file_name(num_experts, shard_intermediate_size // 2,
448
- dtype_str)
480
+ dtype_str, block_quant_shape)
481
482
print(f"Writing best config to {filename}...")
483
with open(filename, "w") as f:
@@ -455,7 +487,7 @@ def save_configs(configs: dict[int, BenchmarkConfig], num_experts: int,
487
488
def main(args: argparse.Namespace):
489
print(args)
458
-
490
+ block_quant_shape = None
491
config = AutoConfig.from_pretrained(
492
args.model, trust_remote_code=args.trust_remote_code)
493
if config.architectures[0] == "DbrxForCausalLM":
@@ -474,6 +506,7 @@ def main(args: argparse.Namespace):
506
topk = config.num_experts_per_tok
507
intermediate_size = config.moe_intermediate_size
508
shard_intermediate_size = 2 * intermediate_size // args.tp_size
509
+ block_quant_shape = config.quantization_config['weight_block_size']
510
else:
511
# Default: Mixtral.
512
E = config.num_local_experts
@@ -511,27 +544,30 @@ def main(args: argparse.Namespace):
544
545
if args.tune:
546
is_fp16 = not (use_fp8_w8a8 or use_int8_w8a16)
514
- search_space = get_configs_compute_bound(is_fp16)
547
+ search_space = get_configs_compute_bound(is_fp16, block_quant_shape)
548
print(f"Start tuning over {len(search_space)} configurations...")
549
550
start = time.time()
551
configs = _distribute(
519
- "tune", [(batch_size, E, shard_intermediate_size, hidden_size,
520
- topk, dtype, use_fp8_w8a8, use_int8_w8a16, search_space)
521
- for batch_size in batch_sizes])
552
+ "tune",
553
+ [(batch_size, E, shard_intermediate_size, hidden_size, topk, dtype,
554
+ use_fp8_w8a8, use_int8_w8a16, search_space, block_quant_shape)
555
+ for batch_size in batch_sizes])
556
best_configs = {
557
M: sort_config(config)
558
for M, config in zip(batch_sizes, configs)
559
}
560
save_configs(best_configs, E, shard_intermediate_size, hidden_size,
527
- topk, dtype, use_fp8_w8a8, use_int8_w8a16)
561
+ topk, dtype, use_fp8_w8a8, use_int8_w8a16,
562
+ block_quant_shape)
563
end = time.time()
564
print(f"Tuning took {end - start:.2f} seconds")
565
else:
566
outputs = _distribute(
532
- "benchmark", [(batch_size, E, shard_intermediate_size, hidden_size,
533
- topk, dtype, use_fp8_w8a8, use_int8_w8a16)
534
- for batch_size in batch_sizes])
567
+ "benchmark",
568
+ [(batch_size, E, shard_intermediate_size, hidden_size, topk, dtype,
569
+ use_fp8_w8a8, use_int8_w8a16, block_quant_shape)
570
+ for batch_size in batch_sizes])
571
572
for batch_size, (config, kernel_time) in zip(batch_sizes, outputs):
573
print(f"Batch size: {batch_size}, config: {config}")
vllm/model_executor/layers/fused_moe/configs/E=256,N=256,device_name=AMD_Instinct_MI300X,dtype=fp8_w8a8,block_shape=[128,128].json
+62
-26
@@ -1,28 +1,28 @@
1
{
2
"1": {
3
"BLOCK_SIZE_M": 16,
4
- "BLOCK_SIZE_N": 32,
4
+ "BLOCK_SIZE_N": 128,
5
"BLOCK_SIZE_K": 256,
6
"GROUP_SIZE_M": 1,
7
- "num_warps": 4,
7
+ "num_warps": 8,
8
"num_stages": 2,
9
"waves_per_eu": 0
10
},
11
"2": {
12
- "BLOCK_SIZE_M": 32,
13
- "BLOCK_SIZE_N": 16,
12
+ "BLOCK_SIZE_M": 16,
13
+ "BLOCK_SIZE_N": 128,
14
"BLOCK_SIZE_K": 256,
15
"GROUP_SIZE_M": 1,
16
- "num_warps": 2,
16
+ "num_warps": 8,
17
"num_stages": 2,
18
"waves_per_eu": 0
19
},
20
"4": {
21
"BLOCK_SIZE_M": 16,
22
- "BLOCK_SIZE_N": 64,
22
+ "BLOCK_SIZE_N": 128,
23
"BLOCK_SIZE_K": 256,
24
"GROUP_SIZE_M": 1,
25
- "num_warps": 4,
25
+ "num_warps": 8,
26
"num_stages": 2,
27
"waves_per_eu": 0
28
},
@@ -31,15 +31,15 @@
31
"BLOCK_SIZE_N": 128,
32
"BLOCK_SIZE_K": 128,
33
"GROUP_SIZE_M": 1,
34
- "num_warps": 4,
34
+ "num_warps": 8,
35
"num_stages": 2,
36
"waves_per_eu": 0
37
},
38
"16": {
39
"BLOCK_SIZE_M": 16,
40
- "BLOCK_SIZE_N": 64,
40
+ "BLOCK_SIZE_N": 128,
41
"BLOCK_SIZE_K": 128,
42
- "GROUP_SIZE_M": 4,
42
+ "GROUP_SIZE_M": 1,
43
"num_warps": 2,
44
"num_stages": 2,
45
"waves_per_eu": 0
@@ -49,13 +49,13 @@
49
"BLOCK_SIZE_N": 128,
50
"BLOCK_SIZE_K": 128,
51
"GROUP_SIZE_M": 1,
52
- "num_warps": 4,
52
+ "num_warps": 2,
53
"num_stages": 2,
54
"waves_per_eu": 0
55
},
56
"32": {
57
"BLOCK_SIZE_M": 16,
58
- "BLOCK_SIZE_N": 64,
58
+ "BLOCK_SIZE_N": 128,
59
"BLOCK_SIZE_K": 128,
60
"GROUP_SIZE_M": 4,
61
"num_warps": 2,
@@ -64,7 +64,7 @@
64
},
65
"48": {
66
"BLOCK_SIZE_M": 16,
67
- "BLOCK_SIZE_N": 64,
67
+ "BLOCK_SIZE_N": 128,
68
"BLOCK_SIZE_K": 128,
69
"GROUP_SIZE_M": 4,
70
"num_warps": 2,
@@ -73,7 +73,7 @@
73
},
74
"64": {
75
"BLOCK_SIZE_M": 16,
76
- "BLOCK_SIZE_N": 64,
76
+ "BLOCK_SIZE_N": 128,
77
"BLOCK_SIZE_K": 128,
78
"GROUP_SIZE_M": 1,
79
"num_warps": 2,
@@ -82,46 +82,82 @@
82
},
83
"96": {
84
"BLOCK_SIZE_M": 16,
85
- "BLOCK_SIZE_N": 64,
85
+ "BLOCK_SIZE_N": 128,
86
"BLOCK_SIZE_K": 128,
87
- "GROUP_SIZE_M": 4,
88
- "num_warps": 4,
87
+ "GROUP_SIZE_M": 8,
88
+ "num_warps": 8,
89
"num_stages": 2,
90
"waves_per_eu": 0
91
},
92
"128": {
93
"BLOCK_SIZE_M": 16,
94
- "BLOCK_SIZE_N": 64,
95
- "BLOCK_SIZE_K": 256,
96
- "GROUP_SIZE_M": 1,
97
- "num_warps": 2,
94
+ "BLOCK_SIZE_N": 128,
95
+ "BLOCK_SIZE_K": 128,
96
+ "GROUP_SIZE_M": 4,
97
+ "num_warps": 4,
98
"num_stages": 2,
99
"waves_per_eu": 0
100
},
101
"256": {
102
"BLOCK_SIZE_M": 16,
103
- "BLOCK_SIZE_N": 64,
103
+ "BLOCK_SIZE_N": 128,
104
"BLOCK_SIZE_K": 128,
105
- "GROUP_SIZE_M": 4,
105
+ "GROUP_SIZE_M": 8,
106
"num_warps": 4,
107
"num_stages": 2,
108
"waves_per_eu": 0
109
},
110
"512": {
111
"BLOCK_SIZE_M": 32,
112
- "BLOCK_SIZE_N": 256,
112
+ "BLOCK_SIZE_N": 128,
113
"BLOCK_SIZE_K": 128,
114
"GROUP_SIZE_M": 8,
115
- "num_warps": 8,
115
+ "num_warps": 4,
116
"num_stages": 2,
117
"waves_per_eu": 0
118
},
119
"1024": {
120
"BLOCK_SIZE_M": 64,
121
+ "BLOCK_SIZE_N": 128,
122
+ "BLOCK_SIZE_K": 128,
123
+ "GROUP_SIZE_M": 8,
124
+ "num_warps": 2,
125
+ "num_stages": 2,
126
+ "waves_per_eu": 0
127
+ },
128
+ "1536": {
129
+ "BLOCK_SIZE_M": 64,
130
+ "BLOCK_SIZE_N": 128,
131
+ "BLOCK_SIZE_K": 128,
132
+ "GROUP_SIZE_M": 4,
133
+ "num_warps": 2,
134
+ "num_stages": 2,
135
+ "waves_per_eu": 0
136
+ },
137
+ "2048": {
138
+ "BLOCK_SIZE_M": 128,
139
"BLOCK_SIZE_N": 256,
140
"BLOCK_SIZE_K": 128,
141
"GROUP_SIZE_M": 8,
124
- "num_warps": 8,
142
+ "num_warps": 4,
143
+ "num_stages": 2,
144
+ "waves_per_eu": 0
145
+ },
146
+ "3072": {
147
+ "BLOCK_SIZE_M": 128,
148
+ "BLOCK_SIZE_N": 256,
149
+ "BLOCK_SIZE_K": 128,
150
+ "GROUP_SIZE_M": 8,
151
+ "num_warps": 4,
152
+ "num_stages": 2,
153
+ "waves_per_eu": 0
154
+ },
155
+ "4096": {
156
+ "BLOCK_SIZE_M": 128,
157
+ "BLOCK_SIZE_N": 256,
158
+ "BLOCK_SIZE_K": 128,
159
+ "GROUP_SIZE_M": 4,
160
+ "num_warps": 4,
161
"num_stages": 2,
162
"waves_per_eu": 0
163
}