blob: f547003573695138d798ebb099caed3cbd105fd8 [file] [log] [blame]
#!/usr/bin/env python
# Copyright 2016 Google Inc.
#
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import print_function
from _benchresult import BenchResult
from argparse import ArgumentParser
from os import path
from queue import Queue
from threading import Thread
import collections
import glob
import math
import re
import subprocess
import sys
__argparse = ArgumentParser(description="""
Executes the skpbench binary with various configs and skps.
Also monitors the output in order to filter out and re-run results that have an
unacceptable stddev.
""")
__argparse.add_argument('-p', '--path',
help='directory to execute ./skpbench from')
__argparse.add_argument('-m', '--max-stddev',
type=float, default=4,
help='initial max allowable relative standard deviation')
__argparse.add_argument('-x', '--suffix',
help='suffix to append on config (e.g. "_before", "_after")')
__argparse.add_argument('-w','--write-path',
help='directory to save .png proofs to disk.')
__argparse.add_argument('-v','--verbosity',
type=int, default=0, help='level of verbosity (0=none to 5=debug)')
__argparse.add_argument('-n', '--samples',
type=int, help='number of samples to collect for each bench')
__argparse.add_argument('-d', '--sample-ms',
type=int, help='duration of each sample')
__argparse.add_argument('--fps',
action='store_true', help='use fps instead of ms')
__argparse.add_argument('-c', '--config',
default='gpu', help='comma- or space-separated list of GPU configs')
__argparse.add_argument('skps',
nargs='+',
help='.skp files or directories to expand for .skp files')
FLAGS = __argparse.parse_args()
class StddevException(Exception):
pass
class Message:
READLINE = 0,
EXIT = 1
def __init__(self, message, value=None):
self.message = message
self.value = value
class SKPBench(Thread):
ARGV = ['skpbench', '--verbosity', str(FLAGS.verbosity)]
if FLAGS.path:
ARGV[0] = path.join(FLAGS.path, ARGV[0])
if FLAGS.samples:
ARGV.extend(['--samples', str(FLAGS.samples)])
if FLAGS.sample_ms:
ARGV.extend(['--sampleMs', str(FLAGS.sample_ms)])
if FLAGS.fps:
ARGV.extend(['--fps', 'true'])
@classmethod
def print_header(cls):
subprocess.call(cls.ARGV + ['--samples', '0'])
def __init__(self, skp, config, max_stddev, best_result=None):
self.skp = skp
self.config = config
self.max_stddev = max_stddev
self.best_result = best_result
self._queue = Queue()
Thread.__init__(self)
def execute(self):
self.start()
while True:
message = self._queue.get()
if message.message == Message.READLINE:
result = BenchResult.match(message.value)
if result:
self.__process_result(result)
else:
print(message.value)
sys.stdout.flush()
continue
if message.message == Message.EXIT:
self.join()
break
def __process_result(self, result):
if not self.best_result or result.stddev <= self.best_result.stddev:
self.best_result = result
elif FLAGS.verbosity >= 1:
print('NOTE: reusing previous result for %s/%s with lower stddev '
'(%s%% instead of %s%%).' %
(result.config, result.bench, self.best_result.stddev,
result.stddev), file=sys.stderr)
if self.max_stddev and self.best_result.stddev > self.max_stddev:
raise StddevException()
self.best_result.print_values(config_suffix=FLAGS.suffix)
def run(self):
"""Called on the background thread.
Launches and reads output from an skpbench process.
"""
commandline = self.ARGV + ['--config', self.config,
'--skp', self.skp,
'--suppressHeader', 'true']
if (FLAGS.write_path):
pngfile = path.join(FLAGS.write_path, self.config,
path.basename(self.skp) + '.png')
commandline.extend(['--png', pngfile])
if (FLAGS.verbosity >= 3):
print(' '.join(commandline), file=sys.stderr)
proc = subprocess.Popen(commandline, stdout=subprocess.PIPE)
for line in iter(proc.stdout.readline, b''):
self._queue.put(Message(Message.READLINE, line.decode('utf-8').rstrip()))
proc.wait()
self._queue.put(Message(Message.EXIT, proc.returncode))
def main():
SKPBench.print_header()
# Delimiter is "," or " ", skip if nested inside parens (e.g. gpu(a=b,c=d)).
DELIMITER = r'[, ](?!(?:[^(]*\([^)]*\))*[^()]*\))'
configs = re.split(DELIMITER, FLAGS.config)
skps = list()
for skp in FLAGS.skps:
if (path.isdir(skp)):
skps.extend(glob.iglob(path.join(skp, '*.skp')))
else:
skps.append(skp)
benches = collections.deque([(skp, config, FLAGS.max_stddev)
for skp in skps
for config in configs])
while benches:
benchargs = benches.popleft()
skpbench = SKPBench(*benchargs)
try:
skpbench.execute()
except StddevException:
retry_max_stddev = skpbench.max_stddev * math.sqrt(2)
if FLAGS.verbosity >= 1:
print('NOTE: stddev too high for %s/%s (%s%%; max=%.2f%%). '
'Re-queuing with max=%.2f%%.' %
(skpbench.best_result.config, skpbench.best_result.bench,
skpbench.best_result.stddev, skpbench.max_stddev,
retry_max_stddev),
file=sys.stderr)
benches.append((skpbench.skp, skpbench.config, retry_max_stddev,
skpbench.best_result))
if __name__ == '__main__':
main()