1#!/usr/bin/env python3 2# 3# Copyright 2019 The Dawn Authors 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17# Based on Angle's perf_test_runner.py 18 19import glob 20import subprocess 21import sys 22import os 23import re 24 25base_path = os.path.abspath( 26 os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')) 27 28# Look for a [Rr]elease build. 29perftests_paths = glob.glob('out/*elease*') 30metric = 'wall_time' 31max_experiments = 10 32 33binary_name = 'dawn_perf_tests' 34if sys.platform == 'win32': 35 binary_name += '.exe' 36 37scores = [] 38 39 40def mean(data): 41 """Return the sample arithmetic mean of data.""" 42 n = len(data) 43 if n < 1: 44 raise ValueError('mean requires at least one data point') 45 return float(sum(data)) / float(n) # in Python 2 use sum(data)/float(n) 46 47 48def sum_of_square_deviations(data, c): 49 """Return sum of square deviations of sequence data.""" 50 ss = sum((float(x) - c)**2 for x in data) 51 return ss 52 53 54def coefficient_of_variation(data): 55 """Calculates the population coefficient of variation.""" 56 n = len(data) 57 if n < 2: 58 raise ValueError('variance requires at least two data points') 59 c = mean(data) 60 ss = sum_of_square_deviations(data, c) 61 pvar = ss / n # the population variance 62 stddev = (pvar**0.5) # population standard deviation 63 return stddev / c 64 65 66def truncated_list(data, n): 67 """Compute a truncated list, n is truncation size""" 68 if len(data) < n * 2: 69 raise ValueError('list not large enough to truncate') 70 return sorted(data)[n:-n] 71 72 73def truncated_mean(data, n): 74 """Compute a truncated mean, n is truncation size""" 75 return mean(truncated_list(data, n)) 76 77 78def truncated_cov(data, n): 79 """Compute a truncated coefficient of variation, n is truncation size""" 80 return coefficient_of_variation(truncated_list(data, n)) 81 82 83# Find most recent binary 84newest_binary = None 85newest_mtime = None 86 87for path in perftests_paths: 88 binary_path = os.path.join(base_path, path, binary_name) 89 if os.path.exists(binary_path): 90 binary_mtime = os.path.getmtime(binary_path) 91 if (newest_binary is None) or (binary_mtime > newest_mtime): 92 newest_binary = binary_path 93 newest_mtime = binary_mtime 94 95perftests_path = newest_binary 96 97if perftests_path == None or not os.path.exists(perftests_path): 98 print('Cannot find Release %s!' % binary_name) 99 sys.exit(1) 100 101if len(sys.argv) >= 2: 102 test_name = sys.argv[1] 103 104print('Using test executable: ' + perftests_path) 105print('Test name: ' + test_name) 106 107 108def get_results(metric, extra_args=[]): 109 process = subprocess.Popen( 110 [perftests_path, '--gtest_filter=' + test_name] + extra_args, 111 stdout=subprocess.PIPE, 112 stderr=subprocess.PIPE) 113 output, err = process.communicate() 114 115 m = re.search(r'Running (\d+) tests', output) 116 if m and int(m.group(1)) > 1: 117 print("Found more than one test result in output:") 118 print(output) 119 sys.exit(3) 120 121 pattern = metric + r'.*= ([0-9.]+)' 122 m = re.findall(pattern, output) 123 if not m: 124 print("Did not find the metric '%s' in the test output:" % metric) 125 print(output) 126 sys.exit(1) 127 128 return [float(value) for value in m] 129 130 131# Calibrate the number of steps 132steps = get_results("steps", ["--calibration"])[0] 133print("running with %d steps." % steps) 134 135# Loop 'max_experiments' times, running the tests. 136for experiment in range(max_experiments): 137 experiment_scores = get_results(metric, ["--override-steps", str(steps)]) 138 139 for score in experiment_scores: 140 sys.stdout.write("%s: %.2f" % (metric, score)) 141 scores.append(score) 142 143 if (len(scores) > 1): 144 sys.stdout.write(", mean: %.2f" % mean(scores)) 145 sys.stdout.write(", variation: %.2f%%" % 146 (coefficient_of_variation(scores) * 100.0)) 147 148 if (len(scores) > 7): 149 truncation_n = len(scores) >> 3 150 sys.stdout.write(", truncated mean: %.2f" % 151 truncated_mean(scores, truncation_n)) 152 sys.stdout.write(", variation: %.2f%%" % 153 (truncated_cov(scores, truncation_n) * 100.0)) 154 155 print("") 156