Skip to content

Commit e634182

Browse files
add UMF benchmarks for fragmentation measurements
redesign of the base class for UMF benchmarks
1 parent be7271c commit e634182

File tree

1 file changed

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

1 file changed

+120
-99
lines changed

devops/scripts/benchmarks/benches/umf.py

+120-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,70 @@ 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 setup(self):
119+
if not isUMFAvailable():
120+
print("UMF prefix path not provided")
121+
return
180122

181-
return results
123+
self.benchmark_bin = os.path.join(options.umf, "benchmark", self.bench_name)
182124

125+
def get_names_of_benchmarks_to_be_run(self, command, env_vars):
126+
list_all_command = command + ["--benchmark_list_tests"]
183127

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

188-
self.lib_to_be_replaced = lib_to_be_replaced
189-
self.replacing_lib = replacing_lib
131+
all_names = self.run_bench(
132+
list_all_command, env_vars, add_sycl=False, ld_library=[self.umf_lib]
133+
).splitlines()
190134

191-
def bin_args(self):
192-
full_args = super().bin_args()
193-
full_args.append(f"--benchmark_filter={self.lib_to_be_replaced}")
135+
if self.is_memory_overhead_checked:
136+
all_names = [
137+
name for name in all_names if re.search("^glibc", name) is None
138+
]
194139

195-
return full_args
140+
return all_names
196141

197-
def get_preloaded_name(self, pool_name) -> str:
198-
new_pool_name = pool_name.replace(self.lib_to_be_replaced, self.replacing_lib)
142+
def run(self, env_vars) -> list[Result]:
143+
command = [f"{self.benchmark_bin}"]
199144

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

202177
def parse_output(self, output):
203178
csv_file = io.StringIO(output)
@@ -208,20 +183,56 @@ def parse_output(self, output):
208183
raise ValueError("Benchmark output does not contain data.")
209184

210185
results = []
186+
211187
for row in reader:
212188
try:
213189
full_name = row[self.col_name]
214190
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)
191+
statistics = None
192+
is_row_matched_to_statistics_type = False
193+
194+
if not self.is_memory_overhead_checked:
195+
statistics = self.get_mean(row)
196+
197+
is_row_matched_to_statistics_type = True
198+
199+
# At this moment, preloaded benchmarks
200+
# do not support memory statitics
201+
if self.is_preloaded:
202+
pool = self.get_preloaded_pool_name(pool)
203+
204+
elif self.is_memory_statistics_included(row):
205+
statistics = self.get_memory_overhead(row)
206+
config = "FRAGMENTATION_" + config
207+
208+
is_row_matched_to_statistics_type = True
209+
210+
if is_row_matched_to_statistics_type:
211+
results.append((config, pool, statistics))
218212

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

223216
return results
224217

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

226237
class GBenchGlibc(GBenchPreloaded):
227238
def __init__(self, bench, replacing_lib):
@@ -251,3 +262,13 @@ def __init__(self, bench):
251262

252263
def extra_env_vars(self) -> dict:
253264
return {"LD_PRELOAD": "libtbbmalloc_proxy.so"}
265+
266+
267+
class GBenchMemoryOverhead(GBench):
268+
def __init__(self, bench):
269+
super().__init__(bench)
270+
271+
self.is_memory_overhead_checked = True
272+
273+
def unit(self):
274+
return "%"

0 commit comments

Comments
 (0)