Skip to content

Commit a4dee05

Browse files
tengqmsolid-shuwen
authored andcommitted
rename modules for benchmark (flagos-ai#2709)
lint style will be fixed in next PR.
1 parent c02148a commit a4dee05

29 files changed

Lines changed: 1122 additions & 97 deletions

benchmark/base.py

Lines changed: 780 additions & 0 deletions
Large diffs are not rendered by default.

benchmark/consts.py

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
import itertools
2+
from dataclasses import asdict, dataclass, fields
3+
from enum import Enum
4+
from typing import List, Optional, Tuple
5+
6+
import torch
7+
8+
FLOAT_DTYPES = [torch.float16, torch.float32, torch.bfloat16]
9+
INT_DTYPES = [torch.int16, torch.int32]
10+
BOOL_DTYPES = [torch.bool]
11+
COMPLEX_DTYPES = [torch.complex64]
12+
13+
DEFAULT_WARMUP_COUNT = 1000
14+
DEFAULT_ITER_COUNT = 100
15+
16+
# LEGACY_SHAPES are maintained for legacy benchmark SIZE settings and may be removed in the future.
17+
# Do not reference this elsewhere.
18+
LEGACY_SHAPES = [i * 64 for i in range(1, 22, 5)]
19+
LEGACY_NON_BLAS_SHAPES = [(1024, shape) for shape in LEGACY_SHAPES]
20+
LEGACY_BLAS_SHAPES = [(16, shape, shape, shape) for shape in LEGACY_SHAPES]
21+
22+
# Default shapes settings
23+
DEFAULT_SHAPES = [
24+
(1024 * 1024 * 1024,), # from perf
25+
(64, 64),
26+
(4096, 4096),
27+
(64, 512, 512),
28+
(1024, 1024, 1024), # from perf
29+
]
30+
31+
32+
def model_shapes():
33+
# batch sizes * seq lengths
34+
BS = [1, 2, 3, 4, 8, 98, 256, 8192]
35+
# attn: wqkv, wo; ffn: w13, w2
36+
NK = [
37+
# extract from llama3-8b
38+
(1024, 4096),
39+
(128256, 4096),
40+
(14336, 4096),
41+
(4096, 14336),
42+
(4096, 4096),
43+
(6144, 4096),
44+
(28672, 4096),
45+
# extract from qwen2.5-7b
46+
(3584, 3584),
47+
(18944, 3584),
48+
(3584, 18944),
49+
(152064, 3584),
50+
(37888, 3584),
51+
(512, 3584),
52+
(4608, 3584),
53+
]
54+
55+
return [(4, bs, n, k) for bs, (n, k) in itertools.product(BS, NK)]
56+
57+
58+
@dataclass
59+
class BenchmarkMetrics:
60+
# Legacy shape information for backward compatibility
61+
# This field corresponds to the 'size' field in the previous version's benchmark.
62+
legacy_shape: Optional[int] = None
63+
# Detailed size info
64+
shape_detail: Optional[Tuple[int, ...]] = None
65+
# Latency base in ms
66+
latency_base: Optional[float] = None
67+
# Latency in ms
68+
latency: Optional[float] = None
69+
gbps_base: Optional[float] = None
70+
gbps: Optional[float] = None
71+
# Speedup over baseline
72+
speedup: Optional[float] = None
73+
# Accuracy over baseline (not implemented yet)
74+
accuracy: Optional[float] = None
75+
# TFLOPS (not implemented yet)
76+
tflops: Optional[float] = None
77+
# Utilization (not implemented yet)
78+
utilization: Optional[float] = None
79+
# Speedup compared to base data
80+
compared_speedup: Optional[float] = None
81+
# Error message
82+
error_msg: Optional[str] = None
83+
84+
85+
ALL_AVAILABLE_METRICS = set(map(lambda x: x.name, fields(BenchmarkMetrics))) - {
86+
"legacy_shape",
87+
"shape_detail",
88+
}
89+
90+
DEFAULT_METRICS = [
91+
metric
92+
for metric in ["latency_base", "latency", "speedup"]
93+
if metric in ALL_AVAILABLE_METRICS
94+
]
95+
96+
97+
def check_metric_dependencies(
98+
requested_metrics: Optional[List[str]],
99+
) -> Optional[List[str]]:
100+
"""
101+
Checks if the requested metrics satisfy their dependencies.
102+
Returns True if the dependencies are satisfied, otherwise False.
103+
"""
104+
# Predefined dependencies between metrics
105+
buildin_dependencies = {
106+
"speedup": ["latency", "latency_base"],
107+
"utilization": ["latency", "tflops"],
108+
}
109+
unsatisfied_metrics = []
110+
if requested_metrics is None:
111+
return unsatisfied_metrics
112+
113+
satisfied_metrics = set()
114+
for metric in requested_metrics:
115+
if metric not in buildin_dependencies:
116+
# If the metric has no dependencies, it's automatically satisfied
117+
satisfied_metrics.add(metric)
118+
else:
119+
required_metrics = buildin_dependencies[metric]
120+
# Check if all dependencies are in the satisfied metrics list
121+
if not all(req in satisfied_metrics for req in required_metrics):
122+
unsatisfied_metrics.append(metric)
123+
else:
124+
satisfied_metrics.add(metric)
125+
return unsatisfied_metrics
126+
127+
128+
def get_recommended_shapes(
129+
op_name: str, op_specified_shapes: Optional[List[Tuple[int, ...]]]
130+
):
131+
def _shapes_sort(shapes):
132+
shapes = [shape if isinstance(shape, tuple) else (shape,) for shape in shapes]
133+
return sorted(shapes, key=lambda x: torch.tensor(x).prod().item())
134+
135+
if op_specified_shapes:
136+
# TODO: handle situation that list as the basic element in shape.
137+
return _shapes_sort(op_specified_shapes)
138+
return _shapes_sort(DEFAULT_SHAPES)
139+
140+
141+
class BenchMode(Enum):
142+
KERNEL = "kernel"
143+
OPERATOR = "operator"
144+
WRAPPER = "wrapper"
145+
146+
147+
class BenchLevel(Enum):
148+
COMPREHENSIVE = "comprehensive"
149+
CORE = "core"
150+
151+
152+
@dataclass
153+
class OperationAttribute:
154+
op_name: str
155+
# Recommended core benchmark shapes for the given operation
156+
recommended_core_shapes: List[Tuple[int, ...]]
157+
shape_desc: str
158+
159+
def __str__(self) -> str:
160+
return (
161+
f"{'Operator name':<40} | {self.op_name}\n"
162+
f"{'Recommended Core Shapes[' + self.shape_desc + ']':<40} | {self.recommended_core_shapes}\n"
163+
)
164+
165+
def to_dict(self) -> dict:
166+
return self.__dict__
167+
168+
169+
def custom_json_encoder(obj):
170+
if isinstance(obj, torch.dtype):
171+
return str(obj)
172+
raise TypeError(f"Object of type {obj.__class__.__name__} is not JSON serializable")
173+
174+
175+
@dataclass
176+
class BenchmarkResult:
177+
"""Record the benchmark result for each operator."""
178+
179+
# Unique name of the operator
180+
op_name: str
181+
dtype: str
182+
mode: str
183+
level: str
184+
# Benchmark results
185+
result: List[BenchmarkMetrics]
186+
187+
def __str__(self) -> str:
188+
header_title = (
189+
f"\nOperator: {self.op_name} Performance Test (dtype={self.dtype}, mode={self.mode},"
190+
f"level={self.level})\n"
191+
)
192+
col_names = [
193+
f"{'Status':<10}",
194+
f"{'Torch Latency (ms)':>20}",
195+
f"{'Gems Latency (ms)':>20}",
196+
f"{'Gems Speedup':>20}",
197+
]
198+
if self.result[0].tflops and self.result[0].tflops != 0.0:
199+
col_names.append(f"{'TFLOPS':>20}")
200+
if self.result[0].gbps is not None:
201+
col_names.append(f"{'Torch GBPS ':>20}")
202+
col_names.append(f"{'Gems GBPS ':>20}")
203+
col_names.append(f"{'Size Detail':>20}\n")
204+
header_col_names = " ".join(col_names)
205+
header_break = "-" * len(header_col_names) + "\n"
206+
header = header_title + header_col_names + header_break
207+
208+
metrics_lines = "".join(self._format_metrics(ele) for ele in self.result)
209+
return header + metrics_lines
210+
211+
def _format_metrics(self, metrics: BenchmarkMetrics) -> str:
212+
# self.gen_legacy_shape(metrics)
213+
# legacy_shape_str = (
214+
# metrics.legacy_shape if metrics.legacy_shape is not None else "N/A"
215+
# )
216+
latency_base_str = (
217+
f"{metrics.latency_base:.6f}" if metrics.latency_base is not None else "N/A"
218+
)
219+
latency_str = f"{metrics.latency:.6f}" if metrics.latency is not None else "N/A"
220+
speedup_str = f"{metrics.speedup:.3f}" if metrics.speedup is not None else "N/A"
221+
torch_gbps_str = (
222+
f"{metrics.gbps_base:.3f}" if metrics.gbps_base is not None else "N/A"
223+
)
224+
gems_gbps_str = f"{metrics.gbps:.3f}" if metrics.gbps is not None else "N/A"
225+
if metrics.tflops and metrics.tflops != 0.0:
226+
tflops_str = (
227+
f"{metrics.tflops:.3f}" if metrics.tflops is not None else "N/A"
228+
)
229+
shape_detail_str = (
230+
metrics.shape_detail if metrics.shape_detail is not None else "N/A"
231+
)
232+
status = "SUCCESS" if metrics.error_msg is None else "FAILED"
233+
data_line = (
234+
f"{status:<10}"
235+
f"{latency_base_str:>20}"
236+
f"{latency_str:>20}"
237+
f"{speedup_str:>20}"
238+
)
239+
if metrics.tflops and metrics.tflops != 0.0:
240+
data_line += f"{tflops_str:>20}"
241+
if metrics.gbps is not None:
242+
data_line += f"{torch_gbps_str:>20}{gems_gbps_str:>20}"
243+
data_line += " " * 10
244+
data_line += f"{shape_detail_str}\n"
245+
return data_line
246+
247+
def gen_legacy_shape(self, metrics: BenchmarkMetrics) -> Optional[int]:
248+
first_shape = (
249+
metrics.shape_detail[0] if isinstance(metrics.shape_detail, list) else None
250+
)
251+
to_record_shape = (
252+
tuple(first_shape) if isinstance(first_shape, torch.Size) else None
253+
)
254+
255+
if to_record_shape in LEGACY_NON_BLAS_SHAPES:
256+
metrics.legacy_shape = to_record_shape[-1]
257+
elif (
258+
isinstance(to_record_shape, tuple)
259+
and len(to_record_shape) == 2
260+
and to_record_shape[0] == 1024
261+
):
262+
metrics.legacy_shape = to_record_shape[-1]
263+
else:
264+
metrics.legacy_shape = None
265+
266+
def to_json(self) -> str:
267+
import json
268+
269+
# Convert to dict and handle tuple serialization for shape_detail
270+
result_dict = asdict(self)
271+
return json.dumps(result_dict, default=custom_json_encoder)
272+
273+
def to_dict(self) -> dict:
274+
return self.__dict__

benchmark/test_abs.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
import pytest
22
import torch
33

4-
from . import attri_util as attrs
5-
from . import performance_utils as base
4+
from . import base, consts
65

76

87
@pytest.mark.abs
98
def test_abs():
109
bench = base.UnaryPointwiseBenchmark(
11-
op_name="abs", torch_op=torch.abs, dtypes=attrs.FLOAT_DTYPES
10+
op_name="abs", torch_op=torch.abs, dtypes=consts.FLOAT_DTYPES
1211
)
1312
bench.run()
1413

1514

1615
@pytest.mark.abs_
1716
def test_abs_inplace():
1817
bench = base.UnaryPointwiseBenchmark(
19-
op_name="abs_", torch_op=torch.abs_, dtypes=attrs.FLOAT_DTYPES, is_inplace=True
18+
op_name="abs_", torch_op=torch.abs_, dtypes=consts.FLOAT_DTYPES, is_inplace=True
2019
)
2120
bench.run()

benchmark/test_absolute.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import pytest
22
import torch
33

4-
from . import attri_util as attrs
5-
from . import performance_utils as base
4+
from . import base, consts
65

76

87
@pytest.mark.absolute
98
def test_absolute():
109
bench = base.UnaryPointwiseBenchmark(
11-
op_name="absolute", torch_op=torch.absolute, dtypes=attrs.FLOAT_DTYPES
10+
op_name="absolute", torch_op=torch.absolute, dtypes=consts.FLOAT_DTYPES
1211
)
1312
bench.run()

benchmark/test_acos.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import pytest
22
import torch
33

4-
from . import attri_util as attrs
5-
from . import performance_utils as base
4+
from . import base, consts
65

76

87
@pytest.mark.acos
98
def test_acos():
109
bench = base.UnaryPointwiseBenchmark(
11-
op_name="acos", torch_op=torch.acos, dtypes=attrs.FLOAT_DTYPES
10+
op_name="acos", torch_op=torch.acos, dtypes=consts.FLOAT_DTYPES
1211
)
1312
bench.run()

benchmark/test_add.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
import pytest
22
import torch
33

4-
from . import attri_util as attrs
5-
from . import performance_utils as base
4+
from . import base, consts
65

76

87
@pytest.mark.add
98
def test_add():
109
bench = base.BinaryPointwiseBenchmark(
1110
op_name="add",
1211
torch_op=torch.add,
13-
dtypes=attrs.FLOAT_DTYPES + attrs.COMPLEX_DTYPES,
12+
dtypes=consts.FLOAT_DTYPES + consts.COMPLEX_DTYPES,
1413
)
1514
bench.run()
1615

@@ -20,7 +19,7 @@ def test_add_inplace():
2019
bench = base.BinaryPointwiseBenchmark(
2120
op_name="add_",
2221
torch_op=lambda a, b: a.add_(b),
23-
dtypes=attrs.FLOAT_DTYPES,
22+
dtypes=consts.FLOAT_DTYPES,
2423
is_inplace=True,
2524
)
2625
bench.run()

benchmark/test_addcdiv.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import pytest
22
import torch
33

4-
from . import attri_util as consts
5-
from . import performance_utils as base
6-
from . import utils
4+
from . import base, consts, utils
75

86

97
def _input_fn(shape, dtype, device):
8+
109
inp1 = utils.generate_tensor_input(shape, dtype, device)
1110
inp2 = utils.generate_tensor_input(shape, dtype, device)
1211
inp3 = utils.generate_tensor_input(shape, dtype, device)

benchmark/test_addcmul.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import pytest
22
import torch
33

4-
from . import attri_util as consts
5-
from . import performance_utils as base
6-
from . import utils
4+
from . import base, consts, utils
75

86

97
def _input_fn(shape, dtype, device):

0 commit comments

Comments
 (0)