-
Notifications
You must be signed in to change notification settings - Fork 245
/
Copy pathbenchmark_runner.py
214 lines (175 loc) · 7.34 KB
/
benchmark_runner.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
# This source code is licensed under the license found in the
# LICENSE file in the root directory of this source tree.
"""
Benchmark Runner
This is the main entry point for the benchmarking application. It reads the YAML configuration
file and orchestrates the entire benchmarking process by:
- Loading and validating benchmark configurations
- Executing benchmark scenarios
- Collecting and processing results
- Generating reports
Usage:
python benchmark_runner.py [config.yaml]
The YAML file should contain all necessary configuration parameters for the benchmarks.
"""
import argparse
from itertools import product
from typing import Any, Dict, List, Optional, Set, Tuple
import yaml
from benchmarks.microbenchmarks.utils import (
BenchmarkConfig,
generate_results_csv,
print_results,
)
def get_shapes_for_config(
shape_configs: List[Dict[str, Any]],
) -> List[Tuple[str, List[int]]]:
"""Get shapes for a given configuration.
Args:
shape_configs: List of shape configurations from YAML
Returns:
List of tuples containing (shape_name, shape)
"""
shapes = []
for shape_config in shape_configs:
name = shape_config["name"]
if name == "custom":
shapes.extend([(name, shape) for shape in shape_config["shapes"]])
else:
raise NotImplementedError(
f"Shape config {name} not supported. Currently only supports custom shapes."
)
return shapes
def get_param_combinations(model_param):
"""Extract all parameter combinations from a model config"""
# Get all shapes
shapes = get_shapes_for_config(model_param["matrix_shapes"])
# Extract all other parameters (excluding matrix_shapes)
base_params = {
key: value for key, value in model_param.items() if key not in ["matrix_shapes"]
}
return shapes, base_params
def get_quantization_sparsity_recipes(
quantization_recipes: List[str], sparsity_recipes: List[str]
) -> Set[Tuple[str, Optional[str]]]:
"""Generate valid quantization and sparsity recipes.
Args:
quantization_recipes: List of quantization recipes
sparsity_recipes: List of sparsity recipes
Returns:
Set of tuples containing (quantization_recipe, sparsity_recipe)
For block sparsity, quantization is always "baseline"
All quantization techniques are also run without sparsity
"""
config_recipes = set()
# Always include baseline without sparsity
config_recipes.add(("baseline", None))
# Add all quantization techniques without sparsity
for quant_config in quantization_recipes:
config_recipes.add((quant_config, None))
# Process combinations of quantization and sparsity
for sparse_config in sparsity_recipes:
if sparse_config is None:
# Skip None sparsity as we've already added all quantization techniques without sparsity
continue
elif "block" in sparse_config:
# For block sparsity, only pair with baseline quantization
config_recipes.add(("baseline", sparse_config))
elif "semi" in sparse_config or "2:4" in sparse_config:
# For semi-sparse, only pair with compatible quantization methods
for quant_config in quantization_recipes:
if (
"marlin" in quant_config
or "int8dq" in quant_config
or "float8dq" in quant_config
or quant_config == "baseline"
):
config_recipes.add((quant_config, sparse_config))
else:
raise ValueError(f"Invalid sparsity recipe: {sparse_config}")
return config_recipes
def load_benchmark_configs(cli_args: argparse.Namespace) -> List[BenchmarkConfig]:
"""Load benchmark configurations from CLI arguments and YAML file."""
with open(cli_args.config, "r") as f:
config = yaml.safe_load(f)
output_dir = config.get("output_dir", "benchmarks/microbenchmarks/results")
benchmark_mode = config.get("benchmark_mode", "inference")
# Create all possible combinations
configs = []
quantization_sparsity_recipes = get_quantization_sparsity_recipes(
config.get("quantization_config_recipe_names", []),
config.get("sparsity_config_recipe_names", []),
)
for model_param in config["model_params"]:
shapes, params = get_param_combinations(model_param)
# Create configs for all combinations
for (quant_config, sparse_config), (shape_name, shape) in product(
quantization_sparsity_recipes,
shapes,
):
configs.append(
BenchmarkConfig(
quantization=quant_config,
sparsity=sparse_config,
params=params,
shape_name=shape_name,
shape=shape,
output_dir=output_dir,
benchmark_mode=benchmark_mode,
)
)
return configs
def run_inference_benchmarks_from_config(configs: List[BenchmarkConfig]) -> None:
"""Run benchmarks using configurations from YAML file"""
from benchmarks.microbenchmarks.benchmark_inference import run as run_inference
results = []
print("----------------- RUNNING BENCHMARKS FOR INFERENCE -----------------------")
for config in configs:
print("----------------------------------------")
try:
print(
f"Running: {config.name} for Quantization: {config.quantization} and Sparsity: {config.sparsity}"
)
result = run_inference(config) # Pass the config object directly
if result is not None: # Only add successful results
results.append(result)
except Exception as e:
import traceback
print(f"Error running benchmark {config.name} with error: {e}")
print(traceback.format_exc())
continue
# Add results to csv if there are any
if results:
generate_results_csv(results, configs[0].output_dir)
# Print results
print_results(results)
else:
print("No benchmark results were collected. All benchmarks failed.")
# TODO: Process results: Speedups:
# 1. For different shapes for same model and quantization
# 2. For different quantizations for same model and shape
# 3. For different models for same quantization
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Run benchmarks from config file")
parser.add_argument(
"--config",
type=str,
required=True,
help="Path to benchmark configuration file",
)
# TODO: Add support for args to override config values and run smaller benchmarks
args = parser.parse_args()
configs = load_benchmark_configs(cli_args=args)
# Run benchmarks
if configs[0].benchmark_mode == "inference":
run_inference_benchmarks_from_config(configs)
elif configs[0].benchmark_mode == "training":
print("Training mode not implemented yet")
else:
raise ValueError(
f"Invalid benchmark mode: {configs[0].benchmark_mode}, choose from inference or training"
)
# TODO: Add support for args to override config values and run smaller benchmarks