Skip to content

Commit b2bd7b2

Browse files
add UMF benchmarks for fragmentation measurements
redesign of the base class for UMF benchmarks run each benchmark separately
1 parent be7271c commit b2bd7b2

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)