Skip to content

Commit 979038a

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 979038a

File tree

1 file changed

+123
-99
lines changed
  • devops/scripts/benchmarks/benches

1 file changed

+123
-99
lines changed

devops/scripts/benchmarks/benches/umf.py

+123-99
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():
@@ -40,96 +41,44 @@ def benchmarks(self) -> list[Benchmark]:
4041
GBenchUmfProxy(self),
4142
GBenchJemalloc(self),
4243
GBenchTbbProxy(self),
44+
GBenchMemoryOverhead(self),
4345
]
4446

4547
return benches
4648

4749

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

5254
self.bench = bench
53-
self.bench_name = name
55+
self.bench_name = "umf-benchmark"
5456
self.oneapi = get_oneapi()
57+
self.umf_lib = options.umf + "lib"
5558

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
63-
64-
def bin_args(self) -> list[str]:
65-
return []
66-
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")
59+
self.num_cols_with_memory = 13
12060

12161
self.col_name = 0
12262
self.col_iterations = 1
12363
self.col_real_time = 2
12464
self.col_cpu_time = 3
12565
self.col_time_unit = 4
66+
self.col_memory_overhead = 11
12667

12768
self.idx_pool = 0
12869
self.idx_config = 1
12970
self.name_separator = "/"
13071

13172
self.col_statistics_time = self.col_real_time
13273

74+
self.is_preloaded = False
75+
self.is_memory_overhead_checked = False
76+
77+
self.lib_to_be_replaced = None
78+
79+
def is_memory_statistics_included(self, data_row):
80+
return len(data_row) == self.num_cols_with_memory
81+
13382
def name(self):
13483
return self.bench_name
13584

@@ -160,44 +109,73 @@ def get_pool_and_config(self, full_name):
160109
def get_mean(self, datarow):
161110
return float(datarow[self.col_statistics_time])
162111

163-
def parse_output(self, output):
164-
csv_file = io.StringIO(output)
165-
reader = csv.reader(csv_file)
112+
def get_memory_overhead(self, datarow):
113+
return float(datarow[self.col_memory_overhead])
166114

167-
data_row = next(reader, None)
168-
if data_row is None:
169-
raise ValueError("Benchmark output does not contain data.")
115+
def extra_env_vars(self) -> dict:
116+
return {}
170117

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}")
118+
def get_tags(self):
119+
return ["UMF", "allocation", "latency", "micro"]
180120

181-
return results
121+
def setup(self):
122+
if not isUMFAvailable():
123+
print("UMF prefix path not provided")
124+
return
182125

126+
self.benchmark_bin = os.path.join(options.umf, "benchmark", self.bench_name)
183127

184-
class GBenchPreloaded(GBench):
185-
def __init__(self, bench, lib_to_be_replaced, replacing_lib):
186-
super().__init__(bench)
128+
def get_names_of_benchmarks_to_be_run(self, command, env_vars):
129+
list_all_command = command + ["--benchmark_list_tests"]
187130

188-
self.lib_to_be_replaced = lib_to_be_replaced
189-
self.replacing_lib = replacing_lib
131+
if self.is_preloaded:
132+
list_all_command += ["--benchmark_filter=" + self.lib_to_be_replaced]
190133

191-
def bin_args(self):
192-
full_args = super().bin_args()
193-
full_args.append(f"--benchmark_filter={self.lib_to_be_replaced}")
134+
all_names = self.run_bench(
135+
list_all_command, env_vars, add_sycl=False, ld_library=[self.umf_lib]
136+
).splitlines()
194137

195-
return full_args
138+
if self.is_memory_overhead_checked:
139+
all_names = [
140+
name for name in all_names if re.search("^glibc", name) is None
141+
]
196142

197-
def get_preloaded_name(self, pool_name) -> str:
198-
new_pool_name = pool_name.replace(self.lib_to_be_replaced, self.replacing_lib)
143+
return all_names
199144

200-
return new_pool_name
145+
def run(self, env_vars) -> list[Result]:
146+
command = [f"{self.benchmark_bin}"]
147+
148+
all_names = self.get_names_of_benchmarks_to_be_run(command, env_vars)
149+
150+
command += self.bin_args()
151+
env_vars.update(self.extra_env_vars())
152+
153+
results = []
154+
155+
for name in all_names:
156+
specific_benchmark = command + ["--benchmark_filter=^" + name + "$"]
157+
158+
result = self.run_bench(
159+
specific_benchmark, env_vars, add_sycl=False, ld_library=[self.umf_lib]
160+
)
161+
162+
parsed = self.parse_output(result)
163+
for r in parsed:
164+
(config, pool, mean) = r
165+
label = f"{config} {pool}"
166+
results.append(
167+
Result(
168+
label=label,
169+
value=mean,
170+
command=command,
171+
env=env_vars,
172+
stdout=result,
173+
unit=self.unit(),
174+
explicit_group=config,
175+
)
176+
)
177+
178+
return results
201179

202180
def parse_output(self, output):
203181
csv_file = io.StringIO(output)
@@ -208,20 +186,56 @@ def parse_output(self, output):
208186
raise ValueError("Benchmark output does not contain data.")
209187

210188
results = []
189+
211190
for row in reader:
212191
try:
213192
full_name = row[self.col_name]
214193
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)
194+
statistics = None
195+
is_row_matched_to_statistics_type = False
196+
197+
if not self.is_memory_overhead_checked:
198+
statistics = self.get_mean(row)
199+
200+
is_row_matched_to_statistics_type = True
201+
202+
# At this moment, preloaded benchmarks
203+
# do not support memory statitics
204+
if self.is_preloaded:
205+
pool = self.get_preloaded_pool_name(pool)
206+
207+
elif self.is_memory_statistics_included(row):
208+
statistics = self.get_memory_overhead(row)
209+
config = "FRAGMENTATION_" + config
210+
211+
is_row_matched_to_statistics_type = True
212+
213+
if is_row_matched_to_statistics_type:
214+
results.append((config, pool, statistics))
218215

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

223219
return results
224220

221+
def teardown(self):
222+
return
223+
224+
225+
class GBenchPreloaded(GBench):
226+
def __init__(self, bench, lib_to_be_replaced, replacing_lib):
227+
super().__init__(bench)
228+
229+
self.is_preloaded = True
230+
231+
self.lib_to_be_replaced = lib_to_be_replaced
232+
self.replacing_lib = replacing_lib
233+
234+
def get_preloaded_pool_name(self, pool_name) -> str:
235+
new_pool_name = pool_name.replace(self.lib_to_be_replaced, self.replacing_lib)
236+
237+
return new_pool_name
238+
225239

226240
class GBenchGlibc(GBenchPreloaded):
227241
def __init__(self, bench, replacing_lib):
@@ -251,3 +265,13 @@ def __init__(self, bench):
251265

252266
def extra_env_vars(self) -> dict:
253267
return {"LD_PRELOAD": "libtbbmalloc_proxy.so"}
268+
269+
270+
class GBenchMemoryOverhead(GBench):
271+
def __init__(self, bench):
272+
super().__init__(bench)
273+
274+
self.is_memory_overhead_checked = True
275+
276+
def unit(self):
277+
return "%"

0 commit comments

Comments
 (0)