@@ -26,15 +26,34 @@ from __future__ import print_function
26
26
import argparse
27
27
import glob
28
28
import os
29
- import re
30
29
import subprocess
31
30
import sys
32
31
33
- from compare_perf_tests import LogParser , create_report
32
+ from compare_perf_tests import LogParser , TestComparator , create_report
33
+
34
+ from imp import load_source
35
+ # import Benchmark_Driver # doesn't work because it misses '.py' extension
36
+ Benchmark_Driver = load_source (
37
+ 'Benchmark_Driver' , os .path .join (os .path .dirname (
38
+ os .path .abspath (__file__ )), 'Benchmark_Driver' ))
39
+ # from Benchmark_Driver import BenchmarkDriver, BenchmarkDoctor, ...
40
+ BenchmarkDriver = Benchmark_Driver .BenchmarkDriver
41
+ BenchmarkDoctor = Benchmark_Driver .BenchmarkDoctor
42
+ MarkdownReportHandler = Benchmark_Driver .MarkdownReportHandler
34
43
35
44
VERBOSE = False
36
45
37
46
47
+ class DriverArgs (object ):
48
+ """Arguments for BenchmarkDriver."""
49
+ def __init__ (self , tests , optimization = 'O' ):
50
+ """Initialize with path to the build-dir and optimization level."""
51
+ self .benchmarks = None
52
+ self .filters = None
53
+ self .tests = os .path .join (tests , 'bin' )
54
+ self .optimization = optimization
55
+
56
+
38
57
def log (msg ):
39
58
print (msg )
40
59
sys .stdout .flush ()
@@ -129,89 +148,61 @@ def test_opt_levels(args):
129
148
return 0
130
149
131
150
132
- def test_performance (opt_level , old_dir , new_dir , threshold , num_samples ,
133
- output_file ):
134
- num_results_dont_differ = 0
135
- iter = 1
136
- to_test = None
137
- prev_num_tests = None
151
+ def measure (driver , tests , i ):
152
+ """Log and measure samples of the tests with the given driver.
138
153
139
- old_lines = ""
140
- new_lines = ""
154
+ Collect increasing number of samples, depending on the iteration.
155
+ """
156
+ num_samples = min (i + 3 , 10 )
157
+ msg = ' Iteration {0} for {1}: num samples = {2}, ' .format (
158
+ i , driver .args .tests , num_samples )
159
+ msg += ('running all tests' if driver .all_tests == tests else
160
+ 're-testing {0} tests' .format (len (tests )))
161
+ log (msg )
162
+ driver .tests = tests
163
+ return driver .run (num_samples = num_samples , sample_time = 0.0025 )
141
164
142
- # #,TEST,SAMPLES,MIN(μs),MAX(μs),MEAN(μs),SD(μs),MEDIAN(μs),PEAK_MEMORY(B)
143
- score_re = re .compile (r"(\d+),([\w.\-]+),\d+,(\d+)" )
144
-
145
- while to_test is None or len (to_test ) > 0 :
146
- tested_benchmarks = set ()
147
-
148
- # (benchmark_name, benchmark_directory) -> (min_value, result_line)
149
- values = {}
150
-
151
- # Run the benchmarks and store the results in 'values'.
152
- for bench_dir in (old_dir , new_dir ):
153
- log (' Iteration ' + str (iter ) + ' for ' + bench_dir +
154
- ': num samples = ' + str (num_samples ) +
155
- (', running all tests' if to_test is None
156
- else ', re-testing ' + str (len (to_test )) + ' tests' ))
157
-
158
- result = get_results (bench_dir , opt_level , num_samples , to_test )
159
- for line in result .splitlines ():
160
- m = score_re .match (line )
161
- if m :
162
- testname = m .group (2 )
163
- val = int (m .group (3 ))
164
- values [(testname , bench_dir )] = (val , line )
165
- tested_benchmarks .add (testname )
166
-
167
- # Some local utility functions
168
-
169
- def bench_in (bench , bench_dir ):
170
- return (bench , bench_dir ) in values
171
-
172
- def within_threshold (bench ):
173
- old_val = values [(bench , old_dir )][0 ]
174
- new_val = values [(bench , new_dir )][0 ]
175
- if not new_val :
176
- return True
177
- f = float (old_val ) / float (new_val )
178
- return f >= 1.0 - threshold and f <= 1.0 + threshold
179
-
180
- def result_line (bench , bench_dir ):
181
- result_line = values [(bench , bench_dir )][1 ]
182
- return result_line + '\n '
183
-
184
- # Check which benchmarks are added/removed and which need to be re-run
185
- to_test = []
186
- for bench in sorted (tested_benchmarks ):
187
- if bench_in (bench , old_dir ) and not bench_in (bench , new_dir ):
188
- old_lines += result_line (bench , old_dir )
189
- elif bench_in (bench , new_dir ) and not bench_in (bench , old_dir ):
190
- new_lines += result_line (bench , new_dir )
191
- elif within_threshold (bench ) or num_results_dont_differ >= 4 :
192
- old_lines += result_line (bench , old_dir )
193
- new_lines += result_line (bench , new_dir )
194
- else :
195
- to_test .append (bench )
196
- if VERBOSE :
197
- log (' test again ' + bench )
198
-
199
- # Track how many times we could not reduce the number of benchmarks
200
- if prev_num_tests == len (to_test ):
201
- num_results_dont_differ += 1
202
- else :
203
- num_results_dont_differ = 0
204
- prev_num_tests = len (to_test )
205
165
206
- # Increase the number of samples for benchmarks which re-run
207
- if num_samples < 10 :
208
- num_samples += 1
166
+ def merge (results , other_results ):
167
+ """"Merge the other PreformanceTestResults into the first dictionary."""
168
+ for test , result in other_results .items ():
169
+ results [test ].merge (result )
170
+ return results
209
171
210
- iter += 1
172
+
173
+ def test_performance (opt_level , old_dir , new_dir , threshold , num_samples ,
174
+ output_file ):
175
+ """Detect performance changes in benchmarks.
176
+
177
+ Start fast with few samples per benchmark and gradually spend more time
178
+ gathering more precise measurements of the change candidates.
179
+ """
180
+
181
+ i , unchanged_length_count = 0 , 0
182
+ old , new = [BenchmarkDriver (DriverArgs (dir , optimization = opt_level ))
183
+ for dir in [old_dir , new_dir ]]
184
+ results = [measure (driver , driver .tests , i ) for driver in [old , new ]]
185
+ tests = TestComparator (results [0 ], results [1 ], threshold )
186
+ changed = tests .decreased + tests .increased
187
+
188
+ while len (changed ) > 0 and unchanged_length_count < 5 :
189
+ i += 1
190
+ if VERBOSE :
191
+ log (' test again: ' + str ([test .name for test in changed ]))
192
+ results = [merge (the_results ,
193
+ measure (driver , [test .name for test in changed ], i ))
194
+ for the_results , driver in zip (results , [old , new ])]
195
+ tests = TestComparator (results [0 ], results [1 ], threshold )
196
+ changed = tests .decreased + tests .increased
197
+
198
+ if len (old .tests ) == len (changed ):
199
+ unchanged_length_count += 1
200
+ else :
201
+ unchanged_length_count = 0
211
202
212
203
log ('' )
213
- return report_results ("Performance: -" + opt_level ,
214
- old_lines , new_lines , threshold * 1.4 , output_file )
204
+ return report_results ("Performance: -" + opt_level , None , None ,
205
+ threshold * 1.4 , output_file , * results )
215
206
216
207
217
208
def get_results (bench_dir , opt_level , num_samples , to_test ):
@@ -274,9 +265,10 @@ def get_codesize(filename):
274
265
return int (data_line .split ('\t ' )[0 ])
275
266
276
267
277
- def report_results (title , old_lines , new_lines , threshold , output_file ):
278
- old_results = LogParser .results_from_string (old_lines )
279
- new_results = LogParser .results_from_string (new_lines )
268
+ def report_results (title , old_lines , new_lines , threshold , output_file ,
269
+ old_results = None , new_results = None ):
270
+ old_results = old_results or LogParser .results_from_string (old_lines )
271
+ new_results = new_results or LogParser .results_from_string (new_lines )
280
272
281
273
print ("------- " + title + " -------" )
282
274
print (create_report (old_results , new_results , threshold , 'git' ))
@@ -332,25 +324,7 @@ performance team (@eeckstein).
332
324
return text
333
325
334
326
335
- class DriverArgs (object ):
336
- def __init__ (self , tests ):
337
- self .benchmarks = None
338
- self .filters = None
339
- self .tests = os .path .join (tests , 'bin' )
340
- self .optimization = 'O'
341
-
342
-
343
327
def check_added (args , output_file = None ):
344
- from imp import load_source
345
- # import Benchmark_Driver # doesn't work because it misses '.py' extension
346
- Benchmark_Driver = load_source (
347
- 'Benchmark_Driver' , os .path .join (os .path .dirname (
348
- os .path .abspath (__file__ )), 'Benchmark_Driver' ))
349
- # from Benchmark_Driver import BenchmarkDriver, BenchmarkDoctor
350
- BenchmarkDriver = Benchmark_Driver .BenchmarkDriver
351
- BenchmarkDoctor = Benchmark_Driver .BenchmarkDoctor
352
- MarkdownReportHandler = Benchmark_Driver .MarkdownReportHandler
353
-
354
328
old = BenchmarkDriver (DriverArgs (args .oldbuilddir [0 ]))
355
329
new = BenchmarkDriver (DriverArgs (args .newbuilddir [0 ]))
356
330
added = set (new .tests ).difference (set (old .tests ))
0 commit comments