13
13
import os
14
14
import csv
15
15
import io
16
+ import re
16
17
17
18
18
19
def isUMFAvailable ():
@@ -45,90 +46,36 @@ def benchmarks(self) -> list[Benchmark]:
45
46
return benches
46
47
47
48
48
- class ComputeUMFBenchmark (Benchmark ):
49
- def __init__ (self , bench , name ):
49
+ class GBench (Benchmark ):
50
+ def __init__ (self , bench ):
50
51
super ().__init__ (bench .directory , bench )
51
52
52
53
self .bench = bench
53
- self .bench_name = name
54
+ self .bench_name = "umf-benchmark"
54
55
self .oneapi = get_oneapi ()
56
+ self .umf_lib = options .umf + "lib"
55
57
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_"
63
59
64
- def bin_args (self ) -> list [str ]:
65
- return []
60
+ self .num_cols_with_memory = 13
66
61
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"
126
68
127
69
self .idx_pool = 0
128
70
self .idx_config = 1
129
71
self .name_separator = "/"
130
72
131
73
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
132
79
133
80
def name (self ):
134
81
return self .bench_name
@@ -139,89 +86,139 @@ def name(self):
139
86
def bin_args (self ):
140
87
return ["--benchmark_format=csv" ]
141
88
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
-
149
89
# these benchmarks are not stable, so set this at a large value
150
90
def stddev_threshold (self ) -> float :
151
91
return 0.2 # 20%
152
92
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
+
153
106
def get_pool_and_config (self , full_name ):
154
107
list_split = full_name .split (self .name_separator , 1 )
155
108
if len (list_split ) != 2 :
156
109
raise ValueError ("Incorrect benchmark name format: " , full_name )
157
110
158
111
return list_split [self .idx_pool ], list_split [self .idx_config ]
159
112
160
- def get_mean (self , datarow ):
113
+ def get_mean_time (self , datarow ):
161
114
return float (datarow [self .col_statistics_time ])
162
115
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 ])
166
118
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 "%"
170
122
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"
180
128
181
- return results
129
+ def get_names_of_benchmarks_to_be_run (self , command , env_vars ):
130
+ list_all_command = command + ["--benchmark_list_tests" ]
182
131
132
+ if self .is_preloaded :
133
+ list_all_command += ["--benchmark_filter=" + self .lib_to_be_replaced ]
183
134
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 ( )
187
138
188
- self .lib_to_be_replaced = lib_to_be_replaced
189
- self .replacing_lib = replacing_lib
139
+ return all_names
190
140
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 } " ]
194
143
195
- return full_args
144
+ all_names = self . get_names_of_benchmarks_to_be_run ( command , env_vars )
196
145
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 () )
199
148
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
201
175
202
176
def parse_output (self , output ):
203
177
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 )
209
179
210
180
results = []
181
+
211
182
for row in reader :
212
183
try :
213
184
full_name = row [self .col_name ]
214
185
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 ))
218
198
219
- results .append ((updated_config , updated_pool , mean ))
220
199
except KeyError as e :
221
200
raise ValueError (f"Error parsing output: { e } " )
222
201
223
202
return results
224
203
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
+
225
222
226
223
class GBenchGlibc (GBenchPreloaded ):
227
224
def __init__ (self , bench , replacing_lib ):
0 commit comments