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)
if os.path.exists(binary_path): parser.add_argument(
binary_mtime = os.path.getmtime(binary_path) '-m',
if (newest_binary is None) or (binary_mtime > newest_mtime): '--metric',
newest_binary = binary_path help='Test metric. Default is "%s".' % DEFAULT_METRIC,
newest_mtime = binary_mtime default=DEFAULT_METRIC)
parser.add_argument(
perftests_path = newest_binary '--experiments',
help='Number of experiments to run. Default is %d.' % DEFAULT_EXPERIMENTS,
if perftests_path == None or not os.path.exists(perftests_path): default=DEFAULT_EXPERIMENTS,
print('Cannot find Release %s!' % binary_name) type=int)
sys.exit(1) 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)
if sys.platform == 'win32': args = parser.parse_args(raw_args)
test_name = 'DrawCallPerfBenchmark.Run/d3d11_null'
else: if args.verbose:
test_name = 'DrawCallPerfBenchmark.Run/gl' logging.basicConfig(level='DEBUG')
if len(sys.argv) >= 2: if sys.platform == 'win32':
test_name = sys.argv[1] args.suite += '.exe'
print('Using test executable: ' + perftests_path) # Find most recent binary
print('Test name: ' + test_name) newest_binary = None
newest_mtime = None
def get_results(metric, extra_args=[]): for path in TEST_SUITE_SEARCH_PATH:
process = subprocess.Popen( binary_path = os.path.join(base_path, path, args.suite)
[perftests_path, '--gtest_filter=' + test_name] + extra_args, if os.path.exists(binary_path):
stdout=subprocess.PIPE, binary_mtime = os.path.getmtime(binary_path)
stderr=subprocess.PIPE) if (newest_binary is None) or (binary_mtime > newest_mtime):
output, err = process.communicate() newest_binary = binary_path
newest_mtime = binary_mtime
m = re.search(r'Running (\d+) tests', output)
if m and int(m.group(1)) > 1: perftests_path = newest_binary
print("Found more than one test result in output:")
print(output) if perftests_path == None or not os.path.exists(perftests_path):
sys.exit(3) print('Cannot find Release %s!' % args.test_suite)
return EXIT_FAILURE
# Results are reported in the format:
# name_backend.metric: story= value units. print('Using test executable: %s' % perftests_path)
pattern = r'\.' + metric + r':.*= ([0-9.]+)' print('Test name: %s' % args.test_name)
m = re.findall(pattern, output)
if not m: def get_results(metric, extra_args=[]):
print("Did not find the metric '%s' in the test output:" % metric) run = [perftests_path, '--gtest_filter=%s' % args.test_name] + extra_args
print(output) logging.info('running %s' % str(run))
sys.exit(1) process = subprocess.Popen(run, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = process.communicate()
return [float(value) for value in m]
m = re.search(r'Running (\d+) tests', output)
if m and int(m.group(1)) > 1:
# Calibrate the number of steps print(output)
steps = get_results("steps", ["--calibration"])[0] raise Exception('Found more than one test result in output')
print("running with %d steps." % steps)
# Results are reported in the format:
# Loop 'max_experiments' times, running the tests. # name_backend.metric: story= value units.
for experiment in range(max_experiments): pattern = r'\.' + metric + r':.*= ([0-9.]+)'
experiment_scores = get_results(metric, ["--steps-per-trial", str(steps)]) logging.debug('searching for %s in output' % pattern)
m = re.findall(pattern, output)
for score in experiment_scores: if not m:
sys.stdout.write("%s: %.2f" % (metric, score)) print(output)
scores.append(score) raise Exception('Did not find the metric "%s" in the test output' % metric)
if (len(scores) > 1): return [float(value) for value in m]
sys.stdout.write(", mean: %.2f" % mean(scores))
sys.stdout.write(", variation: %.2f%%" % (coefficient_of_variation(scores) * 100.0)) # Calibrate the number of steps
steps = get_results("steps_to_run", ["--calibration"])[0]
if (len(scores) > 7): print("running with %d steps." % steps)
truncation_n = len(scores) >> 3
sys.stdout.write(", truncated mean: %.2f" % truncated_mean(scores, truncation_n)) # Loop 'args.experiments' times, running the tests.
sys.stdout.write(", variation: %.2f%%" % (truncated_cov(scores, truncation_n) * 100.0)) for experiment in range(args.experiments):
experiment_scores = get_results(args.metric, ["--steps-per-trial", str(steps)])
print("")
for score in experiment_scores:
sys.stdout.write("%s: %.2f" % (args.metric, score))
scores.append(score)
if (len(scores) > 1):
sys.stdout.write(", mean: %.2f" % mean(scores))
sys.stdout.write(", variation: %.2f%%" %
(coefficient_of_variation(scores) * 100.0))
if (len(scores) > 7):
truncation_n = len(scores) >> 3
sys.stdout.write(", truncated mean: %.2f" % truncated_mean(scores, truncation_n))
sys.stdout.write(", variation: %.2f%%" %
(truncated_cov(scores, truncation_n) * 100.0))
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;
printf("Mean result time: %.4lf ms.\n", 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("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);
} }
mReporter->AddResult(".trial_steps", static_cast<size_t>(mTrialNumStepsPerformed)); if (gCalibration)
mReporter->AddResult(".total_steps", static_cast<size_t>(mTotalNumStepsPerformed)); {
mReporter->AddResult(".steps_to_run", static_cast<size_t>(mStepsToRun));
}
else
{
mReporter->AddResult(".trial_steps", static_cast<size_t>(mTrialNumStepsPerformed));
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