Commit 2c685a41 by Jamie Madill Committed by Commit Bot

Fix the perf test runner calibration.

This path was broken for the white box unit tests. Also adds argparse handling to the runner so we can more flexibly override command line arguments. Previously the broken calibration was causing some of the tests to run only a single test iteration when measuring. This could lead to low quality measurements. Bug: angleproject:5573 Change-Id: Ic1cb2b2553774a361325f290440c40b2ff90db5e Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2672702 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarMohan Maiya <m.maiya@samsung.com> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org>
parent 89f50584
...@@ -10,22 +10,30 @@ ...@@ -10,22 +10,30 @@
# variation of the population continuously. # variation of the population continuously.
# #
import argparse
import glob import glob
import subprocess import logging
import sys
import os import os
import re import re
import subprocess
import sys
base_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')) base_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..'))
# Look for a [Rr]elease build. # Look for a [Rr]elease build.
perftests_paths = glob.glob('out/*elease*') TEST_SUITE_SEARCH_PATH = glob.glob('out/*elease*')
metric = 'wall_time' DEFAULT_METRIC = 'wall_time'
max_experiments = 10 DEFAULT_EXPERIMENTS = 10
DEFAULT_TEST_SUITE = 'angle_perftests'
binary_name = 'angle_perftests'
if sys.platform == 'win32': if sys.platform == 'win32':
binary_name += '.exe' DEFAULT_TEST_NAME = 'DrawCallPerfBenchmark.Run/d3d11_null'
else:
DEFAULT_TEST_NAME = 'DrawCallPerfBenchmark.Run/gl'
EXIT_SUCCESS = 0
EXIT_FAILURE = 1
scores = [] scores = []
...@@ -74,80 +82,102 @@ def truncated_cov(data, n): ...@@ -74,80 +82,102 @@ def truncated_cov(data, n):
return coefficient_of_variation(truncated_list(data, n)) return coefficient_of_variation(truncated_list(data, n))
# Find most recent binary def main(raw_args):
newest_binary = None parser = argparse.ArgumentParser()
newest_mtime = None parser.add_argument(
'--suite',
for path in perftests_paths: help='Test suite binary. Default is "%s".' % DEFAULT_TEST_SUITE,
binary_path = os.path.join(base_path, path, binary_name) default=DEFAULT_TEST_SUITE)
parser.add_argument(
'-m',
'--metric',
help='Test metric. Default is "%s".' % DEFAULT_METRIC,
default=DEFAULT_METRIC)
parser.add_argument(
'--experiments',
help='Number of experiments to run. Default is %d.' % DEFAULT_EXPERIMENTS,
default=DEFAULT_EXPERIMENTS,
type=int)
parser.add_argument('-v', '--verbose', help='Extra verbose logging.', action='store_true')
parser.add_argument('test_name', help='Test to run', default=DEFAULT_TEST_NAME)
args = parser.parse_args(raw_args)
if args.verbose:
logging.basicConfig(level='DEBUG')
if sys.platform == 'win32':
args.suite += '.exe'
# Find most recent binary
newest_binary = None
newest_mtime = None
for path in TEST_SUITE_SEARCH_PATH:
binary_path = os.path.join(base_path, path, args.suite)
if os.path.exists(binary_path): if os.path.exists(binary_path):
binary_mtime = os.path.getmtime(binary_path) binary_mtime = os.path.getmtime(binary_path)
if (newest_binary is None) or (binary_mtime > newest_mtime): if (newest_binary is None) or (binary_mtime > newest_mtime):
newest_binary = binary_path newest_binary = binary_path
newest_mtime = binary_mtime newest_mtime = binary_mtime
perftests_path = newest_binary perftests_path = newest_binary
if perftests_path == None or not os.path.exists(perftests_path): if perftests_path == None or not os.path.exists(perftests_path):
print('Cannot find Release %s!' % binary_name) print('Cannot find Release %s!' % args.test_suite)
sys.exit(1) return EXIT_FAILURE
if sys.platform == 'win32':
test_name = 'DrawCallPerfBenchmark.Run/d3d11_null'
else:
test_name = 'DrawCallPerfBenchmark.Run/gl'
if len(sys.argv) >= 2:
test_name = sys.argv[1]
print('Using test executable: ' + perftests_path)
print('Test name: ' + test_name)
print('Using test executable: %s' % perftests_path)
print('Test name: %s' % args.test_name)
def get_results(metric, extra_args=[]): def get_results(metric, extra_args=[]):
process = subprocess.Popen( run = [perftests_path, '--gtest_filter=%s' % args.test_name] + extra_args
[perftests_path, '--gtest_filter=' + test_name] + extra_args, logging.info('running %s' % str(run))
stdout=subprocess.PIPE, process = subprocess.Popen(run, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stderr=subprocess.PIPE)
output, err = process.communicate() output, err = process.communicate()
m = re.search(r'Running (\d+) tests', output) m = re.search(r'Running (\d+) tests', output)
if m and int(m.group(1)) > 1: if m and int(m.group(1)) > 1:
print("Found more than one test result in output:")
print(output) print(output)
sys.exit(3) raise Exception('Found more than one test result in output')
# Results are reported in the format: # Results are reported in the format:
# name_backend.metric: story= value units. # name_backend.metric: story= value units.
pattern = r'\.' + metric + r':.*= ([0-9.]+)' pattern = r'\.' + metric + r':.*= ([0-9.]+)'
logging.debug('searching for %s in output' % pattern)
m = re.findall(pattern, output) m = re.findall(pattern, output)
if not m: if not m:
print("Did not find the metric '%s' in the test output:" % metric)
print(output) print(output)
sys.exit(1) raise Exception('Did not find the metric "%s" in the test output' % metric)
return [float(value) for value in m] return [float(value) for value in m]
# Calibrate the number of steps
steps = get_results("steps_to_run", ["--calibration"])[0]
print("running with %d steps." % steps)
# Calibrate the number of steps # Loop 'args.experiments' times, running the tests.
steps = get_results("steps", ["--calibration"])[0] for experiment in range(args.experiments):
print("running with %d steps." % steps) experiment_scores = get_results(args.metric, ["--steps-per-trial", str(steps)])
# Loop 'max_experiments' times, running the tests.
for experiment in range(max_experiments):
experiment_scores = get_results(metric, ["--steps-per-trial", str(steps)])
for score in experiment_scores: for score in experiment_scores:
sys.stdout.write("%s: %.2f" % (metric, score)) sys.stdout.write("%s: %.2f" % (args.metric, score))
scores.append(score) scores.append(score)
if (len(scores) > 1): if (len(scores) > 1):
sys.stdout.write(", mean: %.2f" % mean(scores)) sys.stdout.write(", mean: %.2f" % mean(scores))
sys.stdout.write(", variation: %.2f%%" % (coefficient_of_variation(scores) * 100.0)) sys.stdout.write(", variation: %.2f%%" %
(coefficient_of_variation(scores) * 100.0))
if (len(scores) > 7): if (len(scores) > 7):
truncation_n = len(scores) >> 3 truncation_n = len(scores) >> 3
sys.stdout.write(", truncated mean: %.2f" % truncated_mean(scores, truncation_n)) sys.stdout.write(", truncated mean: %.2f" % truncated_mean(scores, truncation_n))
sys.stdout.write(", variation: %.2f%%" % (truncated_cov(scores, truncation_n) * 100.0)) sys.stdout.write(", variation: %.2f%%" %
(truncated_cov(scores, truncation_n) * 100.0))
print("") print("")
return EXIT_SUCCESS
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
...@@ -237,6 +237,7 @@ ANGLEPerfTest::ANGLEPerfTest(const std::string &name, ...@@ -237,6 +237,7 @@ ANGLEPerfTest::ANGLEPerfTest(const std::string &name,
mReporter->RegisterImportantMetric(".gpu_time", units); mReporter->RegisterImportantMetric(".gpu_time", units);
mReporter->RegisterFyiMetric(".trial_steps", "count"); mReporter->RegisterFyiMetric(".trial_steps", "count");
mReporter->RegisterFyiMetric(".total_steps", "count"); mReporter->RegisterFyiMetric(".total_steps", "count");
mReporter->RegisterFyiMetric(".steps_to_run", "count");
} }
ANGLEPerfTest::~ANGLEPerfTest() {} ANGLEPerfTest::~ANGLEPerfTest() {}
...@@ -248,6 +249,14 @@ void ANGLEPerfTest::run() ...@@ -248,6 +249,14 @@ void ANGLEPerfTest::run()
return; return;
} }
if (mStepsToRun <= 0)
{
// We don't call finish between calibration steps when calibrating non-Render tests. The
// Render tests will have already calibrated when this code is run.
calibrateStepsToRun(RunLoopPolicy::RunContinuously);
ASSERT(mStepsToRun > 0);
}
uint32_t numTrials = OneFrame() ? 1 : gTestTrials; uint32_t numTrials = OneFrame() ? 1 : gTestTrials;
if (gVerboseLogging) if (gVerboseLogging)
{ {
...@@ -290,7 +299,14 @@ void ANGLEPerfTest::run() ...@@ -290,7 +299,14 @@ void ANGLEPerfTest::run()
double standardDeviation = std::sqrt(variance); double standardDeviation = std::sqrt(variance);
double coefficientOfVariation = standardDeviation / mean; double coefficientOfVariation = standardDeviation / mean;
if (mean < 0.001)
{
printf("Mean result time: %.4lf ns.\n", mean * 1000.0);
}
else
{
printf("Mean result time: %.4lf ms.\n", mean); printf("Mean result time: %.4lf ms.\n", mean);
}
printf("Coefficient of variation: %.2lf%%\n", coefficientOfVariation * 100.0); printf("Coefficient of variation: %.2lf%%\n", coefficientOfVariation * 100.0);
} }
} }
...@@ -402,8 +418,15 @@ double ANGLEPerfTest::printResults() ...@@ -402,8 +418,15 @@ double ANGLEPerfTest::printResults()
printf("Ran %0.2lf iterations per second\n", fps); printf("Ran %0.2lf iterations per second\n", fps);
} }
if (gCalibration)
{
mReporter->AddResult(".steps_to_run", static_cast<size_t>(mStepsToRun));
}
else
{
mReporter->AddResult(".trial_steps", static_cast<size_t>(mTrialNumStepsPerformed)); mReporter->AddResult(".trial_steps", static_cast<size_t>(mTrialNumStepsPerformed));
mReporter->AddResult(".total_steps", static_cast<size_t>(mTotalNumStepsPerformed)); mReporter->AddResult(".total_steps", static_cast<size_t>(mTotalNumStepsPerformed));
}
// Output histogram JSON set format if enabled. // Output histogram JSON set format if enabled.
double secondsPerStep = elapsedTimeSeconds[0] / static_cast<double>(mTrialNumStepsPerformed); double secondsPerStep = elapsedTimeSeconds[0] / static_cast<double>(mTrialNumStepsPerformed);
...@@ -418,10 +441,9 @@ double ANGLEPerfTest::normalizedTime(size_t value) const ...@@ -418,10 +441,9 @@ double ANGLEPerfTest::normalizedTime(size_t value) const
return static_cast<double>(value) / static_cast<double>(mTrialNumStepsPerformed); return static_cast<double>(value) / static_cast<double>(mTrialNumStepsPerformed);
} }
void ANGLEPerfTest::calibrateStepsToRun() void ANGLEPerfTest::calibrateStepsToRun(RunLoopPolicy policy)
{ {
doRunLoop(gCalibrationTimeSeconds, std::numeric_limits<int>::max(), doRunLoop(gCalibrationTimeSeconds, std::numeric_limits<int>::max(), policy);
RunLoopPolicy::FinishEveryStep);
double elapsedTime = mTimer.getElapsedTime(); double elapsedTime = mTimer.getElapsedTime();
...@@ -447,7 +469,7 @@ void ANGLEPerfTest::calibrateStepsToRun() ...@@ -447,7 +469,7 @@ void ANGLEPerfTest::calibrateStepsToRun()
// Calibration allows the perf test runner script to save some time. // Calibration allows the perf test runner script to save some time.
if (gCalibration) if (gCalibration)
{ {
mReporter->AddResult(".steps", static_cast<size_t>(mStepsToRun)); printResults();
return; return;
} }
} }
...@@ -719,7 +741,9 @@ void ANGLERenderTest::SetUp() ...@@ -719,7 +741,9 @@ void ANGLERenderTest::SetUp()
if (mStepsToRun <= 0) if (mStepsToRun <= 0)
{ {
calibrateStepsToRun(); // Ensure we always call Finish when calibrating Render tests. This completes our work
// beween calibration measurements.
calibrateStepsToRun(RunLoopPolicy::FinishEveryStep);
} }
} }
......
...@@ -107,7 +107,7 @@ class ANGLEPerfTest : public testing::Test, angle::NonCopyable ...@@ -107,7 +107,7 @@ class ANGLEPerfTest : public testing::Test, angle::NonCopyable
virtual void computeGPUTime() {} virtual void computeGPUTime() {}
double printResults(); double printResults();
void calibrateStepsToRun(); void calibrateStepsToRun(RunLoopPolicy policy);
std::string mName; std::string mName;
std::string mBackend; std::string mBackend;
......
...@@ -74,6 +74,7 @@ void ANGLEProcessPerfTestArgs(int *argc, char **argv) ...@@ -74,6 +74,7 @@ void ANGLEProcessPerfTestArgs(int *argc, char **argv)
else if (strcmp("--calibration", argv[argIndex]) == 0) else if (strcmp("--calibration", argv[argIndex]) == 0)
{ {
gCalibration = true; gCalibration = true;
gTestTrials = 0;
} }
else if (strcmp("--steps-per-trial", argv[argIndex]) == 0 && argIndex < *argc - 1) else if (strcmp("--steps-per-trial", argv[argIndex]) == 0 && argIndex < *argc - 1)
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment