blob: 6b4c555a76e25372bcee46b32f47745761f9155b [file] [log] [blame]
csmartdalton4b5179b2016-09-19 11:03:58 -07001#!/usr/bin/env python
2
3# Copyright 2016 Google Inc.
4#
5# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
7
8from __future__ import print_function
csmartdaltond7a9db62016-09-22 05:10:02 -07009from _adb import Adb
csmartdalton4b5179b2016-09-19 11:03:58 -070010from _benchresult import BenchResult
csmartdaltond7a9db62016-09-22 05:10:02 -070011from _hardware import HardwareException, Hardware
csmartdalton4b5179b2016-09-19 11:03:58 -070012from argparse import ArgumentParser
csmartdalton5745d792016-09-22 12:37:21 -070013from multiprocessing import Queue
csmartdaltond7a9db62016-09-22 05:10:02 -070014from threading import Thread, Timer
csmartdalton4b5179b2016-09-19 11:03:58 -070015import collections
16import glob
17import math
18import re
19import subprocess
20import sys
csmartdaltond7a9db62016-09-22 05:10:02 -070021import time
csmartdalton4b5179b2016-09-19 11:03:58 -070022
csmartdaltonbf41fa82016-09-23 11:36:11 -070023__argparse = ArgumentParser(description="""
csmartdalton4b5179b2016-09-19 11:03:58 -070024
25Executes the skpbench binary with various configs and skps.
26
27Also monitors the output in order to filter out and re-run results that have an
28unacceptable stddev.
29
csmartdaltonbf41fa82016-09-23 11:36:11 -070030""")
csmartdalton4b5179b2016-09-19 11:03:58 -070031
csmartdalton2087dda2016-11-09 16:34:53 -050032__argparse.add_argument('skpbench',
Kevin Lubick9c2249f2016-11-10 14:19:00 -050033 help="path to the skpbench binary")
csmartdalton0262b5c2016-09-19 12:04:56 -070034__argparse.add_argument('--adb',
Kevin Lubick9c2249f2016-11-10 14:19:00 -050035 action='store_true', help="execute skpbench over adb")
csmartdalton0262b5c2016-09-19 12:04:56 -070036__argparse.add_argument('-s', '--device-serial',
Kevin Lubick9c2249f2016-11-10 14:19:00 -050037 help="if using adb, ID of the specific device to target "
38 "(only required if more than 1 device is attached)")
csmartdalton4b5179b2016-09-19 11:03:58 -070039__argparse.add_argument('-m', '--max-stddev',
Kevin Lubick9c2249f2016-11-10 14:19:00 -050040 type=float, default=4,
41 help="initial max allowable relative standard deviation")
csmartdalton4b5179b2016-09-19 11:03:58 -070042__argparse.add_argument('-x', '--suffix',
Kevin Lubick9c2249f2016-11-10 14:19:00 -050043 help="suffix to append on config (e.g. '_before', '_after')")
csmartdalton4b5179b2016-09-19 11:03:58 -070044__argparse.add_argument('-w','--write-path',
Kevin Lubick9c2249f2016-11-10 14:19:00 -050045 help="directory to save .png proofs to disk.")
csmartdalton4b5179b2016-09-19 11:03:58 -070046__argparse.add_argument('-v','--verbosity',
Kevin Lubick9c2249f2016-11-10 14:19:00 -050047 type=int, default=1, help="level of verbosity (0=none to 5=debug)")
csmartdalton037adf32016-09-28 13:56:01 -070048__argparse.add_argument('-d', '--duration',
Kevin Lubick9c2249f2016-11-10 14:19:00 -050049 type=int, help="number of milliseconds to run each benchmark")
csmartdalton037adf32016-09-28 13:56:01 -070050__argparse.add_argument('-l', '--sample-ms',
Kevin Lubick9c2249f2016-11-10 14:19:00 -050051 type=int, help="duration of a sample (minimum)")
csmartdaltonc6618dd2016-10-05 08:42:03 -070052__argparse.add_argument('--gpu',
Kevin Lubick9c2249f2016-11-10 14:19:00 -050053 action='store_true',
54 help="perform timing on the gpu clock instead of cpu (gpu work only)")
csmartdalton4b5179b2016-09-19 11:03:58 -070055__argparse.add_argument('--fps',
Kevin Lubick9c2249f2016-11-10 14:19:00 -050056 action='store_true', help="use fps instead of ms")
csmartdalton4b5179b2016-09-19 11:03:58 -070057__argparse.add_argument('-c', '--config',
Brian Salomon50f66d82017-03-17 14:32:05 -040058 default='gl', help="comma- or space-separated list of GPU configs")
csmartdalton49df7702016-11-10 10:36:28 -050059__argparse.add_argument('-a', '--resultsfile',
Kevin Lubick9c2249f2016-11-10 14:19:00 -050060 help="optional file to append results into")
csmartdalton4b5179b2016-09-19 11:03:58 -070061__argparse.add_argument('skps',
Kevin Lubick9c2249f2016-11-10 14:19:00 -050062 nargs='+',
63 help=".skp files or directories to expand for .skp files")
Kevin Lubickcccaef12017-10-13 08:15:09 -040064__argparse.add_argument('--adb_binary', default='adb',
65 help="The name of the adb binary to use.")
csmartdalton4b5179b2016-09-19 11:03:58 -070066
67FLAGS = __argparse.parse_args()
csmartdalton0262b5c2016-09-19 12:04:56 -070068if FLAGS.adb:
69 import _adb_path as _path
Kevin Lubickcccaef12017-10-13 08:15:09 -040070 _path.init(FLAGS.device_serial, FLAGS.adb_binary)
csmartdalton0262b5c2016-09-19 12:04:56 -070071else:
72 import _os_path as _path
csmartdalton4b5179b2016-09-19 11:03:58 -070073
csmartdalton5772eaa2016-10-11 18:28:54 -070074def dump_commandline_if_verbose(commandline):
csmartdaltone4fd0782016-11-09 08:41:23 -080075 if FLAGS.verbosity >= 5:
csmartdalton5772eaa2016-10-11 18:28:54 -070076 quoted = ['\'%s\'' % re.sub(r'([\\\'])', r'\\\1', x) for x in commandline]
77 print(' '.join(quoted), file=sys.stderr)
78
csmartdalton4b5179b2016-09-19 11:03:58 -070079
80class StddevException(Exception):
81 pass
82
83class Message:
84 READLINE = 0,
csmartdaltond7a9db62016-09-22 05:10:02 -070085 POLL_HARDWARE = 1,
86 EXIT = 2
csmartdalton4b5179b2016-09-19 11:03:58 -070087 def __init__(self, message, value=None):
88 self.message = message
89 self.value = value
90
csmartdaltond7a9db62016-09-22 05:10:02 -070091class SubprocessMonitor(Thread):
92 def __init__(self, queue, proc):
93 self._queue = queue
94 self._proc = proc
95 Thread.__init__(self)
96
97 def run(self):
csmartdaltonbf41fa82016-09-23 11:36:11 -070098 """Runs on the background thread."""
csmartdaltond7a9db62016-09-22 05:10:02 -070099 for line in iter(self._proc.stdout.readline, b''):
100 self._queue.put(Message(Message.READLINE, line.decode('utf-8').rstrip()))
101 self._queue.put(Message(Message.EXIT))
102
103class SKPBench:
csmartdalton2087dda2016-11-09 16:34:53 -0500104 ARGV = [FLAGS.skpbench, '--verbosity', str(FLAGS.verbosity)]
csmartdalton037adf32016-09-28 13:56:01 -0700105 if FLAGS.duration:
106 ARGV.extend(['--duration', str(FLAGS.duration)])
csmartdalton4b5179b2016-09-19 11:03:58 -0700107 if FLAGS.sample_ms:
108 ARGV.extend(['--sampleMs', str(FLAGS.sample_ms)])
csmartdaltonc6618dd2016-10-05 08:42:03 -0700109 if FLAGS.gpu:
110 ARGV.extend(['--gpuClock', 'true'])
csmartdalton4b5179b2016-09-19 11:03:58 -0700111 if FLAGS.fps:
112 ARGV.extend(['--fps', 'true'])
csmartdalton0262b5c2016-09-19 12:04:56 -0700113 if FLAGS.adb:
114 if FLAGS.device_serial is None:
Kevin Lubickcccaef12017-10-13 08:15:09 -0400115 ARGV[:0] = [FLAGS.adb_binary, 'shell']
csmartdalton0262b5c2016-09-19 12:04:56 -0700116 else:
Kevin Lubickcccaef12017-10-13 08:15:09 -0400117 ARGV[:0] = [FLAGS.adb_binary, '-s', FLAGS.device_serial, 'shell']
csmartdalton4b5179b2016-09-19 11:03:58 -0700118
119 @classmethod
csmartdalton49df7702016-11-10 10:36:28 -0500120 def get_header(cls, outfile=sys.stdout):
csmartdalton5772eaa2016-10-11 18:28:54 -0700121 commandline = cls.ARGV + ['--duration', '0']
122 dump_commandline_if_verbose(commandline)
csmartdalton49df7702016-11-10 10:36:28 -0500123 out = subprocess.check_output(commandline, stderr=subprocess.STDOUT)
124 return out.rstrip()
csmartdalton5772eaa2016-10-11 18:28:54 -0700125
126 @classmethod
Brian Salomon50f66d82017-03-17 14:32:05 -0400127 def run_warmup(cls, warmup_time, config):
csmartdalton5772eaa2016-10-11 18:28:54 -0700128 if not warmup_time:
129 return
csmartdalton310d72c2016-10-18 09:19:50 -0700130 print('running %i second warmup...' % warmup_time, file=sys.stderr)
csmartdalton5772eaa2016-10-11 18:28:54 -0700131 commandline = cls.ARGV + ['--duration', str(warmup_time * 1000),
Brian Salomon50f66d82017-03-17 14:32:05 -0400132 '--config', config,
csmartdalton5772eaa2016-10-11 18:28:54 -0700133 '--skp', 'warmup']
134 dump_commandline_if_verbose(commandline)
csmartdaltonf2b024d2016-11-09 13:25:23 -0800135 output = subprocess.check_output(commandline, stderr=subprocess.STDOUT)
136
csmartdalton5772eaa2016-10-11 18:28:54 -0700137 # validate the warmup run output.
csmartdaltonf2b024d2016-11-09 13:25:23 -0800138 for line in output.decode('utf-8').split('\n'):
csmartdalton5772eaa2016-10-11 18:28:54 -0700139 match = BenchResult.match(line.rstrip())
140 if match and match.bench == 'warmup':
141 return
142 raise Exception('Invalid warmup output:\n%s' % output)
csmartdalton4b5179b2016-09-19 11:03:58 -0700143
144 def __init__(self, skp, config, max_stddev, best_result=None):
145 self.skp = skp
146 self.config = config
147 self.max_stddev = max_stddev
148 self.best_result = best_result
149 self._queue = Queue()
csmartdaltond7a9db62016-09-22 05:10:02 -0700150 self._proc = None
151 self._monitor = None
152 self._hw_poll_timer = None
csmartdalton4b5179b2016-09-19 11:03:58 -0700153
csmartdaltond7a9db62016-09-22 05:10:02 -0700154 def __enter__(self):
155 return self
156
157 def __exit__(self, exception_type, exception_value, traceback):
158 if self._proc:
159 self.terminate()
160 if self._hw_poll_timer:
161 self._hw_poll_timer.cancel()
162
163 def execute(self, hardware):
164 hardware.sanity_check()
165 self._schedule_hardware_poll()
166
167 commandline = self.ARGV + ['--config', self.config,
168 '--skp', self.skp,
169 '--suppressHeader', 'true']
170 if FLAGS.write_path:
171 pngfile = _path.join(FLAGS.write_path, self.config,
172 _path.basename(self.skp) + '.png')
173 commandline.extend(['--png', pngfile])
csmartdalton5772eaa2016-10-11 18:28:54 -0700174 dump_commandline_if_verbose(commandline)
csmartdaltonf2b024d2016-11-09 13:25:23 -0800175 self._proc = subprocess.Popen(commandline, stdout=subprocess.PIPE,
176 stderr=subprocess.STDOUT)
csmartdaltond7a9db62016-09-22 05:10:02 -0700177 self._monitor = SubprocessMonitor(self._queue, self._proc)
178 self._monitor.start()
179
csmartdalton4b5179b2016-09-19 11:03:58 -0700180 while True:
181 message = self._queue.get()
182 if message.message == Message.READLINE:
183 result = BenchResult.match(message.value)
184 if result:
csmartdaltond7a9db62016-09-22 05:10:02 -0700185 hardware.sanity_check()
186 self._process_result(result)
csmartdaltonf2b024d2016-11-09 13:25:23 -0800187 elif hardware.filter_line(message.value):
csmartdalton310d72c2016-10-18 09:19:50 -0700188 print(message.value, file=sys.stderr)
csmartdalton4b5179b2016-09-19 11:03:58 -0700189 continue
csmartdaltond7a9db62016-09-22 05:10:02 -0700190 if message.message == Message.POLL_HARDWARE:
191 hardware.sanity_check()
192 self._schedule_hardware_poll()
193 continue
csmartdalton4b5179b2016-09-19 11:03:58 -0700194 if message.message == Message.EXIT:
csmartdaltond7a9db62016-09-22 05:10:02 -0700195 self._monitor.join()
196 self._proc.wait()
197 if self._proc.returncode != 0:
198 raise Exception("skpbench exited with nonzero exit code %i" %
199 self._proc.returncode)
200 self._proc = None
csmartdalton4b5179b2016-09-19 11:03:58 -0700201 break
202
csmartdaltond7a9db62016-09-22 05:10:02 -0700203 def _schedule_hardware_poll(self):
204 if self._hw_poll_timer:
205 self._hw_poll_timer.cancel()
206 self._hw_poll_timer = \
207 Timer(1, lambda: self._queue.put(Message(Message.POLL_HARDWARE)))
208 self._hw_poll_timer.start()
209
210 def _process_result(self, result):
csmartdalton4b5179b2016-09-19 11:03:58 -0700211 if not self.best_result or result.stddev <= self.best_result.stddev:
212 self.best_result = result
csmartdaltond7a9db62016-09-22 05:10:02 -0700213 elif FLAGS.verbosity >= 2:
214 print("reusing previous result for %s/%s with lower stddev "
215 "(%s%% instead of %s%%)." %
csmartdalton4b5179b2016-09-19 11:03:58 -0700216 (result.config, result.bench, self.best_result.stddev,
217 result.stddev), file=sys.stderr)
218 if self.max_stddev and self.best_result.stddev > self.max_stddev:
219 raise StddevException()
csmartdalton4b5179b2016-09-19 11:03:58 -0700220
csmartdaltond7a9db62016-09-22 05:10:02 -0700221 def terminate(self):
222 if self._proc:
csmartdaltonc6618dd2016-10-05 08:42:03 -0700223 self._proc.terminate()
csmartdaltond7a9db62016-09-22 05:10:02 -0700224 self._monitor.join()
225 self._proc.wait()
226 self._proc = None
csmartdalton4b5179b2016-09-19 11:03:58 -0700227
csmartdalton49df7702016-11-10 10:36:28 -0500228def emit_result(line, resultsfile=None):
229 print(line)
230 sys.stdout.flush()
231 if resultsfile:
232 print(line, file=resultsfile)
233 resultsfile.flush()
csmartdalton4b5179b2016-09-19 11:03:58 -0700234
csmartdalton49df7702016-11-10 10:36:28 -0500235def run_benchmarks(configs, skps, hardware, resultsfile=None):
236 emit_result(SKPBench.get_header(), resultsfile)
csmartdalton4b5179b2016-09-19 11:03:58 -0700237 benches = collections.deque([(skp, config, FLAGS.max_stddev)
238 for skp in skps
239 for config in configs])
240 while benches:
241 benchargs = benches.popleft()
csmartdaltond7a9db62016-09-22 05:10:02 -0700242 with SKPBench(*benchargs) as skpbench:
243 try:
244 skpbench.execute(hardware)
245 if skpbench.best_result:
csmartdalton49df7702016-11-10 10:36:28 -0500246 emit_result(skpbench.best_result.format(FLAGS.suffix), resultsfile)
csmartdaltond7a9db62016-09-22 05:10:02 -0700247 else:
248 print("WARNING: no result for %s with config %s" %
249 (skpbench.skp, skpbench.config), file=sys.stderr)
csmartdalton4b5179b2016-09-19 11:03:58 -0700250
csmartdaltond7a9db62016-09-22 05:10:02 -0700251 except StddevException:
252 retry_max_stddev = skpbench.max_stddev * math.sqrt(2)
csmartdalton6904b192016-09-29 06:23:23 -0700253 if FLAGS.verbosity >= 1:
csmartdaltond7a9db62016-09-22 05:10:02 -0700254 print("stddev is too high for %s/%s (%s%%, max=%.2f%%), "
255 "re-queuing with max=%.2f%%." %
256 (skpbench.best_result.config, skpbench.best_result.bench,
257 skpbench.best_result.stddev, skpbench.max_stddev,
258 retry_max_stddev),
259 file=sys.stderr)
260 benches.append((skpbench.skp, skpbench.config, retry_max_stddev,
261 skpbench.best_result))
262
263 except HardwareException as exception:
csmartdalton310d72c2016-10-18 09:19:50 -0700264 skpbench.terminate()
csmartdaltone4fd0782016-11-09 08:41:23 -0800265 if FLAGS.verbosity >= 4:
csmartdalton2a961012016-10-11 12:15:13 -0700266 hardware.print_debug_diagnostics()
csmartdaltond7a9db62016-09-22 05:10:02 -0700267 if FLAGS.verbosity >= 1:
268 print("%s; taking a %i second nap..." %
csmartdalton5772eaa2016-10-11 18:28:54 -0700269 (exception.message, exception.sleeptime), file=sys.stderr)
csmartdaltond7a9db62016-09-22 05:10:02 -0700270 benches.appendleft(benchargs) # retry the same bench next time.
csmartdalton5772eaa2016-10-11 18:28:54 -0700271 hardware.sleep(exception.sleeptime)
csmartdaltone4fd0782016-11-09 08:41:23 -0800272 if FLAGS.verbosity >= 4:
csmartdalton310d72c2016-10-18 09:19:50 -0700273 hardware.print_debug_diagnostics()
Brian Salomon50f66d82017-03-17 14:32:05 -0400274 SKPBench.run_warmup(hardware.warmup_time, configs[0])
csmartdaltond7a9db62016-09-22 05:10:02 -0700275
csmartdaltond7a9db62016-09-22 05:10:02 -0700276def main():
277 # Delimiter is ',' or ' ', skip if nested inside parens (e.g. gpu(a=b,c=d)).
278 DELIMITER = r'[, ](?!(?:[^(]*\([^)]*\))*[^()]*\))'
279 configs = re.split(DELIMITER, FLAGS.config)
280 skps = _path.find_skps(FLAGS.skps)
281
282 if FLAGS.adb:
Kevin Lubickcccaef12017-10-13 08:15:09 -0400283 adb = Adb(FLAGS.device_serial, FLAGS.adb_binary,
284 echo=(FLAGS.verbosity >= 5))
csmartdaltone4fd0782016-11-09 08:41:23 -0800285 model = adb.check('getprop ro.product.model').strip()
csmartdaltonbf41fa82016-09-23 11:36:11 -0700286 if model == 'Pixel C':
287 from _hardware_pixel_c import HardwarePixelC
288 hardware = HardwarePixelC(adb)
csmartdalton310d72c2016-10-18 09:19:50 -0700289 elif model == 'Nexus 6P':
290 from _hardware_nexus_6p import HardwareNexus6P
291 hardware = HardwareNexus6P(adb)
csmartdaltond7a9db62016-09-22 05:10:02 -0700292 else:
293 from _hardware_android import HardwareAndroid
294 print("WARNING: %s: don't know how to monitor this hardware; results "
295 "may be unreliable." % model, file=sys.stderr)
296 hardware = HardwareAndroid(adb)
297 else:
298 hardware = Hardware()
299
300 with hardware:
Brian Salomon50f66d82017-03-17 14:32:05 -0400301 SKPBench.run_warmup(hardware.warmup_time, configs[0])
csmartdalton49df7702016-11-10 10:36:28 -0500302 if FLAGS.resultsfile:
303 with open(FLAGS.resultsfile, mode='a+') as resultsfile:
304 run_benchmarks(configs, skps, hardware, resultsfile=resultsfile)
305 else:
306 run_benchmarks(configs, skps, hardware)
csmartdalton4b5179b2016-09-19 11:03:58 -0700307
308
309if __name__ == '__main__':
310 main()