13
13
import os
14
14
import csv
15
15
import io
16
+ import re
16
17
17
18
18
19
def isUMFAvailable ():
@@ -40,96 +41,44 @@ def benchmarks(self) -> list[Benchmark]:
40
41
GBenchUmfProxy (self ),
41
42
GBenchJemalloc (self ),
42
43
GBenchTbbProxy (self ),
44
+ GBenchMemoryOverhead (self ),
43
45
]
44
46
45
47
return benches
46
48
47
49
48
- class ComputeUMFBenchmark (Benchmark ):
49
- def __init__ (self , bench , name ):
50
+ class GBench (Benchmark ):
51
+ def __init__ (self , bench ):
50
52
super ().__init__ (bench .directory , bench )
51
53
52
54
self .bench = bench
53
- self .bench_name = name
55
+ self .bench_name = "umf-benchmark"
54
56
self .oneapi = get_oneapi ()
57
+ self .umf_lib = options .umf + "lib"
55
58
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
120
60
121
61
self .col_name = 0
122
62
self .col_iterations = 1
123
63
self .col_real_time = 2
124
64
self .col_cpu_time = 3
125
65
self .col_time_unit = 4
66
+ self .col_memory_overhead = 11
126
67
127
68
self .idx_pool = 0
128
69
self .idx_config = 1
129
70
self .name_separator = "/"
130
71
131
72
self .col_statistics_time = self .col_real_time
132
73
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
+
133
82
def name (self ):
134
83
return self .bench_name
135
84
@@ -160,44 +109,73 @@ def get_pool_and_config(self, full_name):
160
109
def get_mean (self , datarow ):
161
110
return float (datarow [self .col_statistics_time ])
162
111
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 ])
166
114
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 {}
170
117
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" ]
180
120
181
- return results
121
+ def setup (self ):
122
+ if not isUMFAvailable ():
123
+ print ("UMF prefix path not provided" )
124
+ return
182
125
126
+ self .benchmark_bin = os .path .join (options .umf , "benchmark" , self .bench_name )
183
127
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" ]
187
130
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 ]
190
133
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 ( )
194
137
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
+ ]
196
142
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
199
144
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
201
179
202
180
def parse_output (self , output ):
203
181
csv_file = io .StringIO (output )
@@ -208,20 +186,56 @@ def parse_output(self, output):
208
186
raise ValueError ("Benchmark output does not contain data." )
209
187
210
188
results = []
189
+
211
190
for row in reader :
212
191
try :
213
192
full_name = row [self .col_name ]
214
193
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 ))
218
215
219
- results .append ((updated_config , updated_pool , mean ))
220
216
except KeyError as e :
221
217
raise ValueError (f"Error parsing output: { e } " )
222
218
223
219
return results
224
220
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
+
225
239
226
240
class GBenchGlibc (GBenchPreloaded ):
227
241
def __init__ (self , bench , replacing_lib ):
@@ -251,3 +265,13 @@ def __init__(self, bench):
251
265
252
266
def extra_env_vars (self ) -> dict :
253
267
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