Skip to content

Commit 68e1059

Browse files
authored
Split vLLM benchmark suite (#2682)
1 parent 8bcd5c9 commit 68e1059

11 files changed

Lines changed: 1858 additions & 1695 deletions
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
import random
2+
from itertools import product
3+
from math import ceil
4+
5+
import pytest
6+
import torch
7+
8+
import flag_gems
9+
10+
from . import performance_utils as base
11+
12+
13+
def is_vllm_available():
14+
try:
15+
import vllm._custom_ops as ops # noqa: F401
16+
17+
return True
18+
except ImportError:
19+
return False
20+
21+
22+
VLLM_AVAILABLE = is_vllm_available()
23+
24+
25+
def is_cuda_available():
26+
if flag_gems.device != "cuda":
27+
return False
28+
major, minor = torch.cuda.get_device_capability()
29+
sm_version_num = major * 10 + minor
30+
return sm_version_num >= 90 and sm_version_num < 100
31+
32+
33+
CUDA_AVAILABLE = is_cuda_available()
34+
35+
36+
def to_int8(tensor: torch.Tensor):
37+
return torch.round(tensor.clamp(min=-128, max=127)).to(dtype=torch.int8)
38+
39+
40+
def to_fp8(tensor: torch.Tensor):
41+
finfo = torch.finfo(torch.float8_e4m3fn)
42+
return torch.round(tensor.clamp(min=finfo.min, max=finfo.max)).to(
43+
dtype=torch.float8_e4m3fn
44+
)
45+
46+
47+
class CutlassScaledMMPerfKit:
48+
num_perf_cases = 4
49+
scalar_only_params = []
50+
vector_only_params = []
51+
scalar_and_vector_params = []
52+
block_params = []
53+
54+
@staticmethod
55+
def _get_all_combinations():
56+
# these shapes come from the test file of op `cutlass_scaled_mm` of vLLM
57+
mnk = [
58+
(1, 256, 128),
59+
(1, 16384, 1024),
60+
(1, 24576, 496),
61+
(16, 256, 496),
62+
(16, 16384, 128),
63+
(16, 24576, 4096),
64+
(32, 8192, 4096),
65+
(32, 16384, 4096),
66+
(33, 1024, 1024),
67+
(33, 8192, 128),
68+
(64, 2048, 496),
69+
(64, 16384, 1024),
70+
(100, 8192, 496),
71+
(128, 32768, 4096),
72+
(256, 4096, 4096),
73+
(512, 256, 1024),
74+
(512, 8192, 4096),
75+
(512, 16384, 128),
76+
(512, 24576, 128),
77+
]
78+
scale_shape_types = ["scalar", "vector", "matrix"]
79+
if_use_bias = [True, False]
80+
dtypes = [(torch.int8, torch.float16), (torch.float8_e4m3fn, torch.bfloat16)]
81+
82+
combinations = product(
83+
mnk, scale_shape_types, scale_shape_types, if_use_bias, dtypes
84+
)
85+
return combinations
86+
87+
@classmethod
88+
def _rand_sample(cls, all_params):
89+
random.shuffle(all_params)
90+
count = [0] * 4
91+
for param in all_params:
92+
a_scale_category = param["a_scale_category"]
93+
b_scale_category = param["b_scale_category"]
94+
if a_scale_category == "matrix" and count[0] < cls.num_perf_cases:
95+
count[0] += 1
96+
cls.block_params.append(param)
97+
elif (
98+
a_scale_category == "scalar"
99+
and b_scale_category == "scalar"
100+
and count[1] < cls.num_perf_cases
101+
):
102+
count[1] += 1
103+
cls.scalar_only_params.append(param)
104+
elif (
105+
a_scale_category == "vector"
106+
and b_scale_category == "vector"
107+
and count[2] < cls.num_perf_cases
108+
):
109+
count[2] += 1
110+
cls.vector_only_params.append(param)
111+
elif count[3] < cls.num_perf_cases:
112+
count[3] += 1
113+
cls.scalar_and_vector_params.append(param)
114+
else:
115+
continue
116+
117+
@classmethod
118+
def init_perf_params(cls):
119+
combinations = cls._get_all_combinations()
120+
121+
all_params = []
122+
for (
123+
(M, N, K),
124+
a_scale_category,
125+
b_scale_category,
126+
use_bias,
127+
(in_dtype, out_dtype),
128+
) in combinations:
129+
is_scalar_or_vector_dequant = a_scale_category in [
130+
"scalar",
131+
"vector",
132+
] and b_scale_category in ["scalar", "vector"]
133+
is_block_dequant = (
134+
a_scale_category == "matrix" and b_scale_category == "matrix"
135+
)
136+
137+
if not (is_scalar_or_vector_dequant or is_block_dequant):
138+
continue
139+
140+
if is_block_dequant and (use_bias or M % 4 != 0):
141+
continue
142+
143+
param = {
144+
"M": M,
145+
"N": N,
146+
"K": K,
147+
"a_scale_category": a_scale_category,
148+
"b_scale_category": b_scale_category,
149+
"use_bias": use_bias,
150+
"in_dtype": in_dtype,
151+
"out_dtype": out_dtype,
152+
}
153+
all_params.append(param)
154+
155+
cls._rand_sample(all_params)
156+
157+
@staticmethod
158+
def get_scale_shape(M, N, K, category, is_a_scale=True):
159+
if category == "scalar":
160+
return (1,)
161+
elif category == "vector":
162+
if is_a_scale:
163+
return (M,)
164+
else:
165+
return (N,)
166+
else:
167+
if is_a_scale:
168+
return (M, ceil(K / 128))
169+
else:
170+
return (ceil(K / 128), ceil(N / 128))
171+
172+
173+
class CutlassScaledMMBenchmark(base.Benchmark):
174+
def __init__(self):
175+
extended_dtypes = ["scalar_only", "vector_only", "scalar_and_vector", "block"]
176+
super().__init__(
177+
"cutlass_scaled_mm", torch.ops._C.cutlass_scaled_mm, extended_dtypes
178+
)
179+
self.set_gems(flag_gems.cutlass_scaled_mm)
180+
self.kit = CutlassScaledMMPerfKit
181+
self.kit.init_perf_params()
182+
183+
def set_shapes(self, shape_file_path=None):
184+
self.shapes = []
185+
186+
def get_input_iter(self, dtype):
187+
params = getattr(self.kit, f"{dtype}_params")
188+
189+
for p in params:
190+
M, N, K = p["M"], p["N"], p["K"]
191+
in_dtype = p["in_dtype"]
192+
out_dtype = p["out_dtype"]
193+
a_scale_category = p["a_scale_category"]
194+
b_scale_category = p["b_scale_category"]
195+
196+
if in_dtype == torch.int8:
197+
a = to_int8(torch.randn((M, K), device=flag_gems.device))
198+
b = to_int8(
199+
torch.randn((K, N), device=flag_gems.device).t().contiguous().t()
200+
* 5
201+
)
202+
else:
203+
a = to_fp8(torch.randn((M, K), device=flag_gems.device))
204+
b = to_fp8(
205+
torch.randn((K, N), device=flag_gems.device).t().contiguous().t()
206+
)
207+
208+
a_scale_shape = self.kit.get_scale_shape(M, N, K, a_scale_category)
209+
b_scale_shape = self.kit.get_scale_shape(M, N, K, b_scale_category, False)
210+
211+
scale_a = torch.randn(
212+
a_scale_shape, device=flag_gems.device, dtype=torch.float32
213+
)
214+
scale_b = torch.randn(
215+
b_scale_shape, device=flag_gems.device, dtype=torch.float32
216+
)
217+
218+
scale_a = scale_a.contiguous()
219+
# convert scale_b to col-major
220+
# (for scalar/vector scale_b, this's a identical transformation)
221+
scale_b = scale_b.t().contiguous().t()
222+
223+
bias = None
224+
if p["use_bias"]:
225+
bias = torch.randn((N,), device=flag_gems.device, dtype=out_dtype)
226+
227+
c = torch.empty((M, N), device=flag_gems.device, dtype=out_dtype)
228+
229+
yield (c, a, b, scale_a, scale_b, bias)
230+
231+
232+
@pytest.mark.skipif(
233+
not (VLLM_AVAILABLE and CUDA_AVAILABLE),
234+
reason="requires vLLM and NVIDIA Hopper architecture",
235+
)
236+
@pytest.mark.cutlass_scaled_mm
237+
def test_cutlass_scaled_mm_benchmark():
238+
bench = CutlassScaledMMBenchmark()
239+
bench.run()

0 commit comments

Comments
 (0)