Skip to content

Commit 2d0c3bc

Browse files
add UMF benchmarks for fragmentation measurements
redesign of the base class for UMF benchmarks run each benchmark separately benchmarks using glibc are excluded from the fragmentation benchmark
1 parent be7271c commit 2d0c3bc

File tree

1 file changed

+113
-116
lines changed
  • devops/scripts/benchmarks/benches

1 file changed

+113
-116
lines changed

devops/scripts/benchmarks/benches/umf.py

+113-116
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import os
1414
import csv
1515
import io
16+
import re
1617

1718

1819
def isUMFAvailable():
@@ -45,90 +46,36 @@ def benchmarks(self) -> list[Benchmark]:
4546
return benches
4647

4748

48-
class ComputeUMFBenchmark(Benchmark):
49-
def __init__(self, bench, name):
49+
class GBench(Benchmark):
50+
def __init__(self, bench):
5051
super().__init__(bench.directory, bench)
5152

5253
self.bench = bench
53-
self.bench_name = name
54+
self.bench_name = "umf-benchmark"
5455
self.oneapi = get_oneapi()
56+
self.umf_lib = options.umf + "lib"
5557

56-
self.col_name = None
57-
self.col_iterations = None
58-
self.col_real_time = None
59-
self.col_cpu_time = None
60-
self.col_time_unit = None
61-
62-
self.col_statistics_time = None
58+
self.fragmentation_prefix = "FRAGMENTATION_"
6359

64-
def bin_args(self) -> list[str]:
65-
return []
60+
self.num_cols_with_memory = 13
6661

67-
def extra_env_vars(self) -> dict:
68-
return {}
69-
70-
def setup(self):
71-
if not isUMFAvailable():
72-
print("UMF prefix path not provided")
73-
return
74-
75-
self.benchmark_bin = os.path.join(options.umf, "benchmark", self.bench_name)
76-
77-
def get_tags(self):
78-
return ["UMF", "allocation", "latency", "micro"]
79-
80-
def run(self, env_vars) -> list[Result]:
81-
command = [
82-
f"{self.benchmark_bin}",
83-
]
84-
85-
command += self.bin_args()
86-
env_vars.update(self.extra_env_vars())
87-
88-
result = self.run_bench(
89-
command, env_vars, add_sycl=False, ld_library=[self.oneapi.tbb_lib()]
90-
)
91-
parsed = self.parse_output(result)
92-
results = []
93-
for r in parsed:
94-
(config, pool, mean) = r
95-
label = f"{config} {pool}"
96-
results.append(
97-
Result(
98-
label=label,
99-
value=mean,
100-
command=command,
101-
env=env_vars,
102-
stdout=result,
103-
unit="ns",
104-
explicit_group=config,
105-
)
106-
)
107-
return results
108-
109-
# Implementation with self.col_* indices could lead to the division by None
110-
def get_mean(self, datarow):
111-
raise NotImplementedError()
112-
113-
def teardown(self):
114-
return
115-
116-
117-
class GBench(ComputeUMFBenchmark):
118-
def __init__(self, bench):
119-
super().__init__(bench, "umf-benchmark")
120-
121-
self.col_name = 0
122-
self.col_iterations = 1
123-
self.col_real_time = 2
124-
self.col_cpu_time = 3
125-
self.col_time_unit = 4
62+
self.col_name = "name"
63+
self.col_iterations = "iterations"
64+
self.col_real_time = "real_time"
65+
self.col_cpu_time = "cpu_time"
66+
self.col_time_unit = "time_unit"
67+
self.col_memory_overhead = "memory_overhead"
12668

12769
self.idx_pool = 0
12870
self.idx_config = 1
12971
self.name_separator = "/"
13072

13173
self.col_statistics_time = self.col_real_time
74+
self.col_statistics_memory = self.col_memory_overhead
75+
76+
self.is_preloaded = False
77+
78+
self.lib_to_be_replaced = None
13279

13380
def name(self):
13481
return self.bench_name
@@ -139,89 +86,139 @@ def name(self):
13986
def bin_args(self):
14087
return ["--benchmark_format=csv"]
14188

142-
# the default unit
143-
# might be changed globally with --benchmark_time_unit={ns|us|ms|s}
144-
# the change affects only benchmark where time unit has not been set
145-
# explicitly
146-
def unit(self):
147-
return "ns"
148-
14989
# these benchmarks are not stable, so set this at a large value
15090
def stddev_threshold(self) -> float:
15191
return 0.2 # 20%
15292

93+
def extra_env_vars(self) -> dict:
94+
return {}
95+
96+
def setup(self):
97+
if not isUMFAvailable():
98+
print("UMF prefix path not provided")
99+
return
100+
101+
self.benchmark_bin = os.path.join(options.umf, "benchmark", self.bench_name)
102+
103+
def is_memory_statistics_included(self, data_row):
104+
return len(data_row) == self.num_cols_with_memory
105+
153106
def get_pool_and_config(self, full_name):
154107
list_split = full_name.split(self.name_separator, 1)
155108
if len(list_split) != 2:
156109
raise ValueError("Incorrect benchmark name format: ", full_name)
157110

158111
return list_split[self.idx_pool], list_split[self.idx_config]
159112

160-
def get_mean(self, datarow):
113+
def get_mean_time(self, datarow):
161114
return float(datarow[self.col_statistics_time])
162115

163-
def parse_output(self, output):
164-
csv_file = io.StringIO(output)
165-
reader = csv.reader(csv_file)
116+
def get_memory_overhead(self, datarow):
117+
return float(datarow[self.col_statistics_memory])
166118

167-
data_row = next(reader, None)
168-
if data_row is None:
169-
raise ValueError("Benchmark output does not contain data.")
119+
def get_unit_time_or_overhead(self, config):
120+
if re.search(f"^{self.fragmentation_prefix}", config):
121+
return "%"
170122

171-
results = []
172-
for row in reader:
173-
try:
174-
full_name = row[self.col_name]
175-
pool, config = self.get_pool_and_config(full_name)
176-
mean = self.get_mean(row)
177-
results.append((config, pool, mean))
178-
except KeyError as e:
179-
raise ValueError(f"Error parsing output: {e}")
123+
# the default time unit
124+
# might be changed globally with --benchmark_time_unit={ns|us|ms|s}
125+
# the change affects only benchmark where time unit has not been set
126+
# explicitly
127+
return "ns"
180128

181-
return results
129+
def get_names_of_benchmarks_to_be_run(self, command, env_vars):
130+
list_all_command = command + ["--benchmark_list_tests"]
182131

132+
if self.is_preloaded:
133+
list_all_command += ["--benchmark_filter=" + self.lib_to_be_replaced]
183134

184-
class GBenchPreloaded(GBench):
185-
def __init__(self, bench, lib_to_be_replaced, replacing_lib):
186-
super().__init__(bench)
135+
all_names = self.run_bench(
136+
list_all_command, env_vars, add_sycl=False, ld_library=[self.umf_lib]
137+
).splitlines()
187138

188-
self.lib_to_be_replaced = lib_to_be_replaced
189-
self.replacing_lib = replacing_lib
139+
return all_names
190140

191-
def bin_args(self):
192-
full_args = super().bin_args()
193-
full_args.append(f"--benchmark_filter={self.lib_to_be_replaced}")
141+
def run(self, env_vars) -> list[Result]:
142+
command = [f"{self.benchmark_bin}"]
194143

195-
return full_args
144+
all_names = self.get_names_of_benchmarks_to_be_run(command, env_vars)
196145

197-
def get_preloaded_name(self, pool_name) -> str:
198-
new_pool_name = pool_name.replace(self.lib_to_be_replaced, self.replacing_lib)
146+
command += self.bin_args()
147+
env_vars.update(self.extra_env_vars())
199148

200-
return new_pool_name
149+
results = []
150+
151+
for name in all_names:
152+
specific_benchmark = command + ["--benchmark_filter=^" + name + "$"]
153+
154+
result = self.run_bench(
155+
specific_benchmark, env_vars, add_sycl=False, ld_library=[self.umf_lib]
156+
)
157+
158+
parsed = self.parse_output(result)
159+
for r in parsed:
160+
(explicit_group, pool, value) = r
161+
label = f"{explicit_group} {pool}"
162+
results.append(
163+
Result(
164+
label=label,
165+
value=value,
166+
command=command,
167+
env=env_vars,
168+
stdout=result,
169+
unit=self.get_unit_time_or_overhead(explicit_group),
170+
explicit_group=explicit_group,
171+
)
172+
)
173+
174+
return results
201175

202176
def parse_output(self, output):
203177
csv_file = io.StringIO(output)
204-
reader = csv.reader(csv_file)
205-
206-
data_row = next(reader, None)
207-
if data_row is None:
208-
raise ValueError("Benchmark output does not contain data.")
178+
reader = csv.DictReader(csv_file)
209179

210180
results = []
181+
211182
for row in reader:
212183
try:
213184
full_name = row[self.col_name]
214185
pool, config = self.get_pool_and_config(full_name)
215-
mean = self.get_mean(row)
216-
updated_pool = self.get_preloaded_name(pool)
217-
updated_config = self.get_preloaded_name(config)
186+
statistics_time = self.get_mean_time(row)
187+
188+
if self.is_preloaded:
189+
pool = self.get_preloaded_pool_name(pool)
190+
191+
results.append((config, pool, statistics_time))
192+
193+
if self.is_memory_statistics_included(row):
194+
statistics_overhead = self.get_memory_overhead(row)
195+
config = self.fragmentation_prefix + config
196+
197+
results.append((config, pool, statistics_overhead))
218198

219-
results.append((updated_config, updated_pool, mean))
220199
except KeyError as e:
221200
raise ValueError(f"Error parsing output: {e}")
222201

223202
return results
224203

204+
def teardown(self):
205+
return
206+
207+
208+
class GBenchPreloaded(GBench):
209+
def __init__(self, bench, lib_to_be_replaced, replacing_lib):
210+
super().__init__(bench)
211+
212+
self.is_preloaded = True
213+
214+
self.lib_to_be_replaced = lib_to_be_replaced
215+
self.replacing_lib = replacing_lib
216+
217+
def get_preloaded_pool_name(self, pool_name) -> str:
218+
new_pool_name = pool_name.replace(self.lib_to_be_replaced, self.replacing_lib)
219+
220+
return new_pool_name
221+
225222

226223
class GBenchGlibc(GBenchPreloaded):
227224
def __init__(self, bench, replacing_lib):

0 commit comments

Comments
 (0)