Jamie Madill | d6dfe67 | 2015-11-20 12:18:12 -0500 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | # |
| 3 | # Copyright 2015 The ANGLE Project Authors. All rights reserved. |
| 4 | # Use of this source code is governed by a BSD-style license that can be |
| 5 | # found in the LICENSE file. |
| 6 | # |
| 7 | # perf_test_runner.py: |
| 8 | # Helper script for running and analyzing perftest results. Runs the |
| 9 | # tests in an infinite batch, printing out the mean and standard |
| 10 | # deviation of the population continuously. |
| 11 | # |
| 12 | |
| 13 | import subprocess |
| 14 | import sys |
| 15 | import os |
Olli Etuaho | d39f893 | 2015-12-17 17:10:02 +0200 | [diff] [blame] | 16 | import re |
| 17 | |
| 18 | base_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')) |
Jamie Madill | d6dfe67 | 2015-11-20 12:18:12 -0500 | [diff] [blame] | 19 | |
| 20 | # You might have to re-order these to find the specific version you want. |
Olli Etuaho | d39f893 | 2015-12-17 17:10:02 +0200 | [diff] [blame] | 21 | perftests_paths = [ |
Jamie Madill | 1fbc59f | 2016-02-24 15:25:51 -0500 | [diff] [blame] | 22 | os.path.join('out', 'Release_x64'), |
Olli Etuaho | d39f893 | 2015-12-17 17:10:02 +0200 | [diff] [blame] | 23 | os.path.join('out', 'Release'), |
Jamie Madill | 1079bb2 | 2016-11-17 10:00:39 -0800 | [diff] [blame] | 24 | os.path.join('gyp', 'Release_x64'), |
| 25 | os.path.join('gyp', 'Release_Win32') |
Olli Etuaho | d39f893 | 2015-12-17 17:10:02 +0200 | [diff] [blame] | 26 | ] |
Jamie Madill | d6dfe67 | 2015-11-20 12:18:12 -0500 | [diff] [blame] | 27 | metric = 'score' |
| 28 | |
Jamie Madill | e4857c7 | 2016-04-21 14:13:53 -0400 | [diff] [blame] | 29 | # TODO(jmadill): Linux binaries |
| 30 | binary_name = 'angle_perftests.exe' |
| 31 | |
Jamie Madill | d6dfe67 | 2015-11-20 12:18:12 -0500 | [diff] [blame] | 32 | scores = [] |
| 33 | |
| 34 | # Danke to http://stackoverflow.com/a/27758326 |
| 35 | def mean(data): |
| 36 | """Return the sample arithmetic mean of data.""" |
| 37 | n = len(data) |
| 38 | if n < 1: |
| 39 | raise ValueError('mean requires at least one data point') |
| 40 | return float(sum(data))/float(n) # in Python 2 use sum(data)/float(n) |
| 41 | |
| 42 | def _ss(data): |
| 43 | """Return sum of square deviations of sequence data.""" |
| 44 | c = mean(data) |
| 45 | ss = sum((float(x)-c)**2 for x in data) |
| 46 | return ss |
| 47 | |
| 48 | def pstdev(data): |
| 49 | """Calculates the population standard deviation.""" |
| 50 | n = len(data) |
| 51 | if n < 2: |
| 52 | raise ValueError('variance requires at least two data points') |
| 53 | ss = _ss(data) |
| 54 | pvar = ss/n # the population variance |
| 55 | return pvar**0.5 |
| 56 | |
| 57 | def truncated_list(data, n): |
| 58 | """Compute a truncated list, n is truncation size""" |
| 59 | if len(data) < n * 2: |
| 60 | raise ValueError('list not large enough to truncate') |
| 61 | return sorted(data)[n:-n] |
| 62 | |
| 63 | def truncated_mean(data, n): |
| 64 | return mean(truncated_list(data, n)) |
| 65 | |
| 66 | def truncated_stddev(data, n): |
| 67 | return pstdev(truncated_list(data, n)) |
| 68 | |
Jamie Madill | e4857c7 | 2016-04-21 14:13:53 -0400 | [diff] [blame] | 69 | # Find most recent binary |
| 70 | newest_binary = None |
| 71 | newest_mtime = None |
Jamie Madill | d6dfe67 | 2015-11-20 12:18:12 -0500 | [diff] [blame] | 72 | |
Jamie Madill | d6dfe67 | 2015-11-20 12:18:12 -0500 | [diff] [blame] | 73 | for path in perftests_paths: |
Jamie Madill | e4857c7 | 2016-04-21 14:13:53 -0400 | [diff] [blame] | 74 | binary_path = os.path.join(base_path, path, binary_name) |
Jamie Madill | e4857c7 | 2016-04-21 14:13:53 -0400 | [diff] [blame] | 75 | if os.path.exists(binary_path): |
Jamie Madill | 1079bb2 | 2016-11-17 10:00:39 -0800 | [diff] [blame] | 76 | binary_mtime = os.path.getmtime(binary_path) |
Jamie Madill | e4857c7 | 2016-04-21 14:13:53 -0400 | [diff] [blame] | 77 | if (newest_binary is None) or (binary_mtime > newest_mtime): |
| 78 | newest_binary = binary_path |
| 79 | newest_mtime = binary_mtime |
| 80 | |
| 81 | perftests_path = newest_binary |
Jamie Madill | d6dfe67 | 2015-11-20 12:18:12 -0500 | [diff] [blame] | 82 | |
Jamie Madill | 1079bb2 | 2016-11-17 10:00:39 -0800 | [diff] [blame] | 83 | if perftests_path == None or not os.path.exists(perftests_path): |
| 84 | print("Cannot find Release angle_perftests.exe!") |
Jamie Madill | d6dfe67 | 2015-11-20 12:18:12 -0500 | [diff] [blame] | 85 | sys.exit(1) |
| 86 | |
| 87 | test_name = "DrawCallPerfBenchmark.Run/d3d11_null" |
| 88 | |
| 89 | if len(sys.argv) >= 2: |
| 90 | test_name = sys.argv[1] |
| 91 | |
Olli Etuaho | 967cb3b | 2015-12-17 18:37:16 +0200 | [diff] [blame] | 92 | print('Using test executable: ' + perftests_path) |
| 93 | print('Test name: ' + test_name) |
| 94 | |
Jamie Madill | d6dfe67 | 2015-11-20 12:18:12 -0500 | [diff] [blame] | 95 | # Infinite loop of running the tests. |
| 96 | while True: |
| 97 | output = subprocess.check_output([perftests_path, '--gtest_filter=' + test_name]) |
| 98 | |
| 99 | start_index = output.find(metric + "=") |
| 100 | if start_index == -1: |
Olli Etuaho | d39f893 | 2015-12-17 17:10:02 +0200 | [diff] [blame] | 101 | print("Did not find the score of the specified test in output:") |
| 102 | print(output) |
Jamie Madill | d6dfe67 | 2015-11-20 12:18:12 -0500 | [diff] [blame] | 103 | sys.exit(1) |
| 104 | |
| 105 | start_index += len(metric) + 2 |
| 106 | |
| 107 | end_index = output[start_index:].find(" ") |
| 108 | if end_index == -1: |
Olli Etuaho | d39f893 | 2015-12-17 17:10:02 +0200 | [diff] [blame] | 109 | print("Error parsing output:") |
| 110 | print(output) |
Jamie Madill | d6dfe67 | 2015-11-20 12:18:12 -0500 | [diff] [blame] | 111 | sys.exit(2) |
| 112 | |
Olli Etuaho | d39f893 | 2015-12-17 17:10:02 +0200 | [diff] [blame] | 113 | m = re.search('Running (\d+) tests', output) |
| 114 | if m and int(m.group(1)) > 1: |
| 115 | print("Found more than one test result in output:") |
| 116 | print(output) |
| 117 | sys.exit(3) |
| 118 | |
Jamie Madill | d6dfe67 | 2015-11-20 12:18:12 -0500 | [diff] [blame] | 119 | end_index += start_index |
| 120 | |
| 121 | score = int(output[start_index:end_index]) |
| 122 | sys.stdout.write("score: " + str(score)) |
| 123 | |
| 124 | scores.append(score) |
| 125 | sys.stdout.write(", mean: " + str(mean(scores))) |
| 126 | |
| 127 | if (len(scores) > 1): |
| 128 | sys.stdout.write(", stddev: " + str(pstdev(scores))) |
| 129 | |
| 130 | if (len(scores) > 7): |
| 131 | trucation_n = len(scores) >> 3 |
| 132 | sys.stdout.write(", truncated mean: " + str(truncated_mean(scores, trucation_n))) |
| 133 | sys.stdout.write(", stddev: " + str(truncated_stddev(scores, trucation_n))) |
| 134 | |
| 135 | print("") |