1313import os
1414import csv
1515import io
16+ import re
1617
1718
1819def isUMFAvailable ():
@@ -45,90 +46,36 @@ def benchmarks(self) -> list[Benchmark]:
4546 return benches
4647
4748
48- class ComputeUMFBenchmark (Benchmark ):
49- def __init__ (self , bench , name ):
49+ class GBench (Benchmark ):
50+ def __init__ (self , bench ):
5051 super ().__init__ (bench .directory , bench )
5152
5253 self .bench = bench
53- self .bench_name = name
54+ self .bench_name = "umf-benchmark"
5455 self .oneapi = get_oneapi ()
56+ self .umf_lib = options .umf + "lib"
5557
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_"
6359
64- def bin_args (self ) -> list [str ]:
65- return []
60+ self .num_cols_with_memory = 13
6661
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"
12668
12769 self .idx_pool = 0
12870 self .idx_config = 1
12971 self .name_separator = "/"
13072
13173 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
13279
13380 def name (self ):
13481 return self .bench_name
@@ -139,89 +86,139 @@ def name(self):
13986 def bin_args (self ):
14087 return ["--benchmark_format=csv" ]
14188
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-
14989 # these benchmarks are not stable, so set this at a large value
15090 def stddev_threshold (self ) -> float :
15191 return 0.2 # 20%
15292
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+
153106 def get_pool_and_config (self , full_name ):
154107 list_split = full_name .split (self .name_separator , 1 )
155108 if len (list_split ) != 2 :
156109 raise ValueError ("Incorrect benchmark name format: " , full_name )
157110
158111 return list_split [self .idx_pool ], list_split [self .idx_config ]
159112
160- def get_mean (self , datarow ):
113+ def get_mean_time (self , datarow ):
161114 return float (datarow [self .col_statistics_time ])
162115
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 ])
166118
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 "%"
170122
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"
180128
181- return results
129+ def get_names_of_benchmarks_to_be_run (self , command , env_vars ):
130+ list_all_command = command + ["--benchmark_list_tests" ]
182131
132+ if self .is_preloaded :
133+ list_all_command += ["--benchmark_filter=" + self .lib_to_be_replaced ]
183134
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 ( )
187138
188- self .lib_to_be_replaced = lib_to_be_replaced
189- self .replacing_lib = replacing_lib
139+ return all_names
190140
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 } " ]
194143
195- return full_args
144+ all_names = self . get_names_of_benchmarks_to_be_run ( command , env_vars )
196145
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 () )
199148
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
201175
202176 def parse_output (self , output ):
203177 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 )
209179
210180 results = []
181+
211182 for row in reader :
212183 try :
213184 full_name = row [self .col_name ]
214185 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 ))
218198
219- results .append ((updated_config , updated_pool , mean ))
220199 except KeyError as e :
221200 raise ValueError (f"Error parsing output: { e } " )
222201
223202 return results
224203
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+
225222
226223class GBenchGlibc (GBenchPreloaded ):
227224 def __init__ (self , bench , replacing_lib ):
0 commit comments