OLD | NEW |
(Empty) | |
| 1 #!/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 |
| 8 from __future__ import print_function |
| 9 from _benchresult import BenchResult |
| 10 from argparse import ArgumentParser |
| 11 from os import path |
| 12 from queue import Queue |
| 13 from threading import Thread |
| 14 import collections |
| 15 import glob |
| 16 import math |
| 17 import re |
| 18 import subprocess |
| 19 import sys |
| 20 |
| 21 __argparse = ArgumentParser(description=""" |
| 22 |
| 23 Executes the skpbench binary with various configs and skps. |
| 24 |
| 25 Also monitors the output in order to filter out and re-run results that have an |
| 26 unacceptable stddev. |
| 27 |
| 28 """) |
| 29 |
| 30 __argparse.add_argument('-p', '--path', |
| 31 help='directory to execute ./skpbench from') |
| 32 __argparse.add_argument('-m', '--max-stddev', |
| 33 type=float, default=4, |
| 34 help='initial max allowable relative standard deviation') |
| 35 __argparse.add_argument('-x', '--suffix', |
| 36 help='suffix to append on config (e.g. "_before", "_after")') |
| 37 __argparse.add_argument('-w','--write-path', |
| 38 help='directory to save .png proofs to disk.') |
| 39 __argparse.add_argument('-v','--verbosity', |
| 40 type=int, default=0, help='level of verbosity (0=none to 5=debug)') |
| 41 __argparse.add_argument('-n', '--samples', |
| 42 type=int, help='number of samples to collect for each bench') |
| 43 __argparse.add_argument('-d', '--sample-ms', |
| 44 type=int, help='duration of each sample') |
| 45 __argparse.add_argument('--fps', |
| 46 action='store_true', help='use fps instead of ms') |
| 47 __argparse.add_argument('-c', '--config', |
| 48 default='gpu', help='comma- or space-separated list of GPU configs') |
| 49 __argparse.add_argument('skps', |
| 50 nargs='+', |
| 51 help='.skp files or directories to expand for .skp files') |
| 52 |
| 53 FLAGS = __argparse.parse_args() |
| 54 |
| 55 |
| 56 class StddevException(Exception): |
| 57 pass |
| 58 |
| 59 class Message: |
| 60 READLINE = 0, |
| 61 EXIT = 1 |
| 62 def __init__(self, message, value=None): |
| 63 self.message = message |
| 64 self.value = value |
| 65 |
| 66 class SKPBench(Thread): |
| 67 ARGV = ['skpbench', '--verbosity', str(FLAGS.verbosity)] |
| 68 if FLAGS.path: |
| 69 ARGV[0] = path.join(FLAGS.path, ARGV[0]) |
| 70 if FLAGS.samples: |
| 71 ARGV.extend(['--samples', str(FLAGS.samples)]) |
| 72 if FLAGS.sample_ms: |
| 73 ARGV.extend(['--sampleMs', str(FLAGS.sample_ms)]) |
| 74 if FLAGS.fps: |
| 75 ARGV.extend(['--fps', 'true']) |
| 76 |
| 77 @classmethod |
| 78 def print_header(cls): |
| 79 subprocess.call(cls.ARGV + ['--samples', '0']) |
| 80 |
| 81 def __init__(self, skp, config, max_stddev, best_result=None): |
| 82 self.skp = skp |
| 83 self.config = config |
| 84 self.max_stddev = max_stddev |
| 85 self.best_result = best_result |
| 86 self._queue = Queue() |
| 87 Thread.__init__(self) |
| 88 |
| 89 def execute(self): |
| 90 self.start() |
| 91 while True: |
| 92 message = self._queue.get() |
| 93 if message.message == Message.READLINE: |
| 94 result = BenchResult.match(message.value) |
| 95 if result: |
| 96 self.__process_result(result) |
| 97 else: |
| 98 print(message.value) |
| 99 sys.stdout.flush() |
| 100 continue |
| 101 if message.message == Message.EXIT: |
| 102 self.join() |
| 103 break |
| 104 |
| 105 def __process_result(self, result): |
| 106 if not self.best_result or result.stddev <= self.best_result.stddev: |
| 107 self.best_result = result |
| 108 elif FLAGS.verbosity >= 1: |
| 109 print('NOTE: reusing previous result for %s/%s with lower stddev ' |
| 110 '(%s%% instead of %s%%).' % |
| 111 (result.config, result.bench, self.best_result.stddev, |
| 112 result.stddev), file=sys.stderr) |
| 113 if self.max_stddev and self.best_result.stddev > self.max_stddev: |
| 114 raise StddevException() |
| 115 self.best_result.print_values(config_suffix=FLAGS.suffix) |
| 116 |
| 117 def run(self): |
| 118 """Called on the background thread. |
| 119 |
| 120 Launches and reads output from an skpbench process. |
| 121 |
| 122 """ |
| 123 commandline = self.ARGV + ['--config', self.config, |
| 124 '--skp', self.skp, |
| 125 '--suppressHeader', 'true'] |
| 126 if (FLAGS.write_path): |
| 127 pngfile = path.join(FLAGS.write_path, self.config, |
| 128 path.basename(self.skp) + '.png') |
| 129 commandline.extend(['--png', pngfile]) |
| 130 if (FLAGS.verbosity >= 3): |
| 131 print(' '.join(commandline), file=sys.stderr) |
| 132 proc = subprocess.Popen(commandline, stdout=subprocess.PIPE) |
| 133 for line in iter(proc.stdout.readline, b''): |
| 134 self._queue.put(Message(Message.READLINE, line.decode('utf-8').rstrip())) |
| 135 proc.wait() |
| 136 self._queue.put(Message(Message.EXIT, proc.returncode)) |
| 137 |
| 138 |
| 139 def main(): |
| 140 SKPBench.print_header() |
| 141 |
| 142 # Delimiter is "," or " ", skip if nested inside parens (e.g. gpu(a=b,c=d)). |
| 143 DELIMITER = r'[, ](?!(?:[^(]*\([^)]*\))*[^()]*\))' |
| 144 configs = re.split(DELIMITER, FLAGS.config) |
| 145 |
| 146 skps = list() |
| 147 for skp in FLAGS.skps: |
| 148 if (path.isdir(skp)): |
| 149 skps.extend(glob.iglob(path.join(skp, '*.skp'))) |
| 150 else: |
| 151 skps.append(skp) |
| 152 |
| 153 benches = collections.deque([(skp, config, FLAGS.max_stddev) |
| 154 for skp in skps |
| 155 for config in configs]) |
| 156 while benches: |
| 157 benchargs = benches.popleft() |
| 158 skpbench = SKPBench(*benchargs) |
| 159 try: |
| 160 skpbench.execute() |
| 161 |
| 162 except StddevException: |
| 163 retry_max_stddev = skpbench.max_stddev * math.sqrt(2) |
| 164 if FLAGS.verbosity >= 1: |
| 165 print('NOTE: stddev too high for %s/%s (%s%%; max=%.2f%%). ' |
| 166 'Re-queuing with max=%.2f%%.' % |
| 167 (skpbench.best_result.config, skpbench.best_result.bench, |
| 168 skpbench.best_result.stddev, skpbench.max_stddev, |
| 169 retry_max_stddev), |
| 170 file=sys.stderr) |
| 171 benches.append((skpbench.skp, skpbench.config, retry_max_stddev, |
| 172 skpbench.best_result)) |
| 173 |
| 174 |
| 175 if __name__ == '__main__': |
| 176 main() |
OLD | NEW |