summaryrefslogtreecommitdiffstats
path: root/lib/libcxx/utils/google-benchmark/tools
diff options
context:
space:
mode:
authorpatrick <patrick@openbsd.org>2019-02-04 16:55:44 +0000
committerpatrick <patrick@openbsd.org>2019-02-04 16:55:44 +0000
commit76c648e7a477ffb2a882ad5ffe523269bd9a3f6a (patch)
tree29d319d598650bab04e4f58e5e8769567e33091e /lib/libcxx/utils/google-benchmark/tools
parentImport libc++abi 7.0.1. (diff)
downloadwireguard-openbsd-76c648e7a477ffb2a882ad5ffe523269bd9a3f6a.tar.xz
wireguard-openbsd-76c648e7a477ffb2a882ad5ffe523269bd9a3f6a.zip
Import libc++ 7.0.1.
Diffstat (limited to 'lib/libcxx/utils/google-benchmark/tools')
-rwxr-xr-xlib/libcxx/utils/google-benchmark/tools/compare.py373
-rw-r--r--lib/libcxx/utils/google-benchmark/tools/compare_bench.py11
-rw-r--r--lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test1_run1.json44
-rw-r--r--lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test1_run2.json48
-rw-r--r--lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test2_run.json81
-rw-r--r--lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test3_run0.json39
-rw-r--r--lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test3_run1.json39
-rw-r--r--lib/libcxx/utils/google-benchmark/tools/gbench/report.py256
-rwxr-xr-xlib/libcxx/utils/google-benchmark/tools/strip_asm.py151
9 files changed, 1008 insertions, 34 deletions
diff --git a/lib/libcxx/utils/google-benchmark/tools/compare.py b/lib/libcxx/utils/google-benchmark/tools/compare.py
new file mode 100755
index 00000000000..d27e24b3492
--- /dev/null
+++ b/lib/libcxx/utils/google-benchmark/tools/compare.py
@@ -0,0 +1,373 @@
+#!/usr/bin/env python
+
+"""
+compare.py - versatile benchmark output compare tool
+"""
+
+import argparse
+from argparse import ArgumentParser
+import sys
+import gbench
+from gbench import util, report
+from gbench.util import *
+
+
+def check_inputs(in1, in2, flags):
+ """
+ Perform checking on the user provided inputs and diagnose any abnormalities
+ """
+ in1_kind, in1_err = classify_input_file(in1)
+ in2_kind, in2_err = classify_input_file(in2)
+ output_file = find_benchmark_flag('--benchmark_out=', flags)
+ output_type = find_benchmark_flag('--benchmark_out_format=', flags)
+ if in1_kind == IT_Executable and in2_kind == IT_Executable and output_file:
+ print(("WARNING: '--benchmark_out=%s' will be passed to both "
+ "benchmarks causing it to be overwritten") % output_file)
+ if in1_kind == IT_JSON and in2_kind == IT_JSON and len(flags) > 0:
+ print("WARNING: passing optional flags has no effect since both "
+ "inputs are JSON")
+ if output_type is not None and output_type != 'json':
+ print(("ERROR: passing '--benchmark_out_format=%s' to 'compare.py`"
+ " is not supported.") % output_type)
+ sys.exit(1)
+
+
+def create_parser():
+ parser = ArgumentParser(
+ description='versatile benchmark output compare tool')
+
+ utest = parser.add_argument_group()
+ utest.add_argument(
+ '--no-utest',
+ dest='utest',
+ default=True,
+ action="store_false",
+ help="The tool can do a two-tailed Mann-Whitney U test with the null hypothesis that it is equally likely that a randomly selected value from one sample will be less than or greater than a randomly selected value from a second sample.\nWARNING: requires **LARGE** (no less than {}) number of repetitions to be meaningful!\nThe test is being done by default, if at least {} repetitions were done.\nThis option can disable the U Test.".format(report.UTEST_OPTIMAL_REPETITIONS, report.UTEST_MIN_REPETITIONS))
+ alpha_default = 0.05
+ utest.add_argument(
+ "--alpha",
+ dest='utest_alpha',
+ default=alpha_default,
+ type=float,
+ help=("significance level alpha. if the calculated p-value is below this value, then the result is said to be statistically significant and the null hypothesis is rejected.\n(default: %0.4f)") %
+ alpha_default)
+
+ subparsers = parser.add_subparsers(
+ help='This tool has multiple modes of operation:',
+ dest='mode')
+
+ parser_a = subparsers.add_parser(
+ 'benchmarks',
+ help='The most simple use-case, compare all the output of these two benchmarks')
+ baseline = parser_a.add_argument_group(
+ 'baseline', 'The benchmark baseline')
+ baseline.add_argument(
+ 'test_baseline',
+ metavar='test_baseline',
+ type=argparse.FileType('r'),
+ nargs=1,
+ help='A benchmark executable or JSON output file')
+ contender = parser_a.add_argument_group(
+ 'contender', 'The benchmark that will be compared against the baseline')
+ contender.add_argument(
+ 'test_contender',
+ metavar='test_contender',
+ type=argparse.FileType('r'),
+ nargs=1,
+ help='A benchmark executable or JSON output file')
+ parser_a.add_argument(
+ 'benchmark_options',
+ metavar='benchmark_options',
+ nargs=argparse.REMAINDER,
+ help='Arguments to pass when running benchmark executables')
+
+ parser_b = subparsers.add_parser(
+ 'filters', help='Compare filter one with the filter two of benchmark')
+ baseline = parser_b.add_argument_group(
+ 'baseline', 'The benchmark baseline')
+ baseline.add_argument(
+ 'test',
+ metavar='test',
+ type=argparse.FileType('r'),
+ nargs=1,
+ help='A benchmark executable or JSON output file')
+ baseline.add_argument(
+ 'filter_baseline',
+ metavar='filter_baseline',
+ type=str,
+ nargs=1,
+ help='The first filter, that will be used as baseline')
+ contender = parser_b.add_argument_group(
+ 'contender', 'The benchmark that will be compared against the baseline')
+ contender.add_argument(
+ 'filter_contender',
+ metavar='filter_contender',
+ type=str,
+ nargs=1,
+ help='The second filter, that will be compared against the baseline')
+ parser_b.add_argument(
+ 'benchmark_options',
+ metavar='benchmark_options',
+ nargs=argparse.REMAINDER,
+ help='Arguments to pass when running benchmark executables')
+
+ parser_c = subparsers.add_parser(
+ 'benchmarksfiltered',
+ help='Compare filter one of first benchmark with filter two of the second benchmark')
+ baseline = parser_c.add_argument_group(
+ 'baseline', 'The benchmark baseline')
+ baseline.add_argument(
+ 'test_baseline',
+ metavar='test_baseline',
+ type=argparse.FileType('r'),
+ nargs=1,
+ help='A benchmark executable or JSON output file')
+ baseline.add_argument(
+ 'filter_baseline',
+ metavar='filter_baseline',
+ type=str,
+ nargs=1,
+ help='The first filter, that will be used as baseline')
+ contender = parser_c.add_argument_group(
+ 'contender', 'The benchmark that will be compared against the baseline')
+ contender.add_argument(
+ 'test_contender',
+ metavar='test_contender',
+ type=argparse.FileType('r'),
+ nargs=1,
+ help='The second benchmark executable or JSON output file, that will be compared against the baseline')
+ contender.add_argument(
+ 'filter_contender',
+ metavar='filter_contender',
+ type=str,
+ nargs=1,
+ help='The second filter, that will be compared against the baseline')
+ parser_c.add_argument(
+ 'benchmark_options',
+ metavar='benchmark_options',
+ nargs=argparse.REMAINDER,
+ help='Arguments to pass when running benchmark executables')
+
+ return parser
+
+
+def main():
+ # Parse the command line flags
+ parser = create_parser()
+ args, unknown_args = parser.parse_known_args()
+ if args.mode is None:
+ parser.print_help()
+ exit(1)
+ assert not unknown_args
+ benchmark_options = args.benchmark_options
+
+ if args.mode == 'benchmarks':
+ test_baseline = args.test_baseline[0].name
+ test_contender = args.test_contender[0].name
+ filter_baseline = ''
+ filter_contender = ''
+
+ # NOTE: if test_baseline == test_contender, you are analyzing the stdev
+
+ description = 'Comparing %s to %s' % (test_baseline, test_contender)
+ elif args.mode == 'filters':
+ test_baseline = args.test[0].name
+ test_contender = args.test[0].name
+ filter_baseline = args.filter_baseline[0]
+ filter_contender = args.filter_contender[0]
+
+ # NOTE: if filter_baseline == filter_contender, you are analyzing the
+ # stdev
+
+ description = 'Comparing %s to %s (from %s)' % (
+ filter_baseline, filter_contender, args.test[0].name)
+ elif args.mode == 'benchmarksfiltered':
+ test_baseline = args.test_baseline[0].name
+ test_contender = args.test_contender[0].name
+ filter_baseline = args.filter_baseline[0]
+ filter_contender = args.filter_contender[0]
+
+ # NOTE: if test_baseline == test_contender and
+ # filter_baseline == filter_contender, you are analyzing the stdev
+
+ description = 'Comparing %s (from %s) to %s (from %s)' % (
+ filter_baseline, test_baseline, filter_contender, test_contender)
+ else:
+ # should never happen
+ print("Unrecognized mode of operation: '%s'" % args.mode)
+ parser.print_help()
+ exit(1)
+
+ check_inputs(test_baseline, test_contender, benchmark_options)
+
+ options_baseline = []
+ options_contender = []
+
+ if filter_baseline and filter_contender:
+ options_baseline = ['--benchmark_filter=%s' % filter_baseline]
+ options_contender = ['--benchmark_filter=%s' % filter_contender]
+
+ # Run the benchmarks and report the results
+ json1 = json1_orig = gbench.util.run_or_load_benchmark(
+ test_baseline, benchmark_options + options_baseline)
+ json2 = json2_orig = gbench.util.run_or_load_benchmark(
+ test_contender, benchmark_options + options_contender)
+
+ # Now, filter the benchmarks so that the difference report can work
+ if filter_baseline and filter_contender:
+ replacement = '[%s vs. %s]' % (filter_baseline, filter_contender)
+ json1 = gbench.report.filter_benchmark(
+ json1_orig, filter_baseline, replacement)
+ json2 = gbench.report.filter_benchmark(
+ json2_orig, filter_contender, replacement)
+
+ # Diff and output
+ output_lines = gbench.report.generate_difference_report(
+ json1, json2, args.utest, args.utest_alpha)
+ print(description)
+ for ln in output_lines:
+ print(ln)
+
+
+import unittest
+
+
+class TestParser(unittest.TestCase):
+ def setUp(self):
+ self.parser = create_parser()
+ testInputs = os.path.join(
+ os.path.dirname(
+ os.path.realpath(__file__)),
+ 'gbench',
+ 'Inputs')
+ self.testInput0 = os.path.join(testInputs, 'test1_run1.json')
+ self.testInput1 = os.path.join(testInputs, 'test1_run2.json')
+
+ def test_benchmarks_basic(self):
+ parsed = self.parser.parse_args(
+ ['benchmarks', self.testInput0, self.testInput1])
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'benchmarks')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertFalse(parsed.benchmark_options)
+
+ def test_benchmarks_basic_without_utest(self):
+ parsed = self.parser.parse_args(
+ ['--no-utest', 'benchmarks', self.testInput0, self.testInput1])
+ self.assertFalse(parsed.utest)
+ self.assertEqual(parsed.utest_alpha, 0.05)
+ self.assertEqual(parsed.mode, 'benchmarks')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertFalse(parsed.benchmark_options)
+
+ def test_benchmarks_basic_with_utest_alpha(self):
+ parsed = self.parser.parse_args(
+ ['--alpha=0.314', 'benchmarks', self.testInput0, self.testInput1])
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.utest_alpha, 0.314)
+ self.assertEqual(parsed.mode, 'benchmarks')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertFalse(parsed.benchmark_options)
+
+ def test_benchmarks_basic_without_utest_with_utest_alpha(self):
+ parsed = self.parser.parse_args(
+ ['--no-utest', '--alpha=0.314', 'benchmarks', self.testInput0, self.testInput1])
+ self.assertFalse(parsed.utest)
+ self.assertEqual(parsed.utest_alpha, 0.314)
+ self.assertEqual(parsed.mode, 'benchmarks')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertFalse(parsed.benchmark_options)
+
+ def test_benchmarks_with_remainder(self):
+ parsed = self.parser.parse_args(
+ ['benchmarks', self.testInput0, self.testInput1, 'd'])
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'benchmarks')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertEqual(parsed.benchmark_options, ['d'])
+
+ def test_benchmarks_with_remainder_after_doubleminus(self):
+ parsed = self.parser.parse_args(
+ ['benchmarks', self.testInput0, self.testInput1, '--', 'e'])
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'benchmarks')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertEqual(parsed.benchmark_options, ['e'])
+
+ def test_filters_basic(self):
+ parsed = self.parser.parse_args(
+ ['filters', self.testInput0, 'c', 'd'])
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'filters')
+ self.assertEqual(parsed.test[0].name, self.testInput0)
+ self.assertEqual(parsed.filter_baseline[0], 'c')
+ self.assertEqual(parsed.filter_contender[0], 'd')
+ self.assertFalse(parsed.benchmark_options)
+
+ def test_filters_with_remainder(self):
+ parsed = self.parser.parse_args(
+ ['filters', self.testInput0, 'c', 'd', 'e'])
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'filters')
+ self.assertEqual(parsed.test[0].name, self.testInput0)
+ self.assertEqual(parsed.filter_baseline[0], 'c')
+ self.assertEqual(parsed.filter_contender[0], 'd')
+ self.assertEqual(parsed.benchmark_options, ['e'])
+
+ def test_filters_with_remainder_after_doubleminus(self):
+ parsed = self.parser.parse_args(
+ ['filters', self.testInput0, 'c', 'd', '--', 'f'])
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'filters')
+ self.assertEqual(parsed.test[0].name, self.testInput0)
+ self.assertEqual(parsed.filter_baseline[0], 'c')
+ self.assertEqual(parsed.filter_contender[0], 'd')
+ self.assertEqual(parsed.benchmark_options, ['f'])
+
+ def test_benchmarksfiltered_basic(self):
+ parsed = self.parser.parse_args(
+ ['benchmarksfiltered', self.testInput0, 'c', self.testInput1, 'e'])
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'benchmarksfiltered')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.filter_baseline[0], 'c')
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertEqual(parsed.filter_contender[0], 'e')
+ self.assertFalse(parsed.benchmark_options)
+
+ def test_benchmarksfiltered_with_remainder(self):
+ parsed = self.parser.parse_args(
+ ['benchmarksfiltered', self.testInput0, 'c', self.testInput1, 'e', 'f'])
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'benchmarksfiltered')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.filter_baseline[0], 'c')
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertEqual(parsed.filter_contender[0], 'e')
+ self.assertEqual(parsed.benchmark_options[0], 'f')
+
+ def test_benchmarksfiltered_with_remainder_after_doubleminus(self):
+ parsed = self.parser.parse_args(
+ ['benchmarksfiltered', self.testInput0, 'c', self.testInput1, 'e', '--', 'g'])
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'benchmarksfiltered')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.filter_baseline[0], 'c')
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertEqual(parsed.filter_contender[0], 'e')
+ self.assertEqual(parsed.benchmark_options[0], 'g')
+
+
+if __name__ == '__main__':
+ # unittest.main()
+ main()
+
+# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
+# kate: tab-width: 4; replace-tabs on; indent-width 4; tab-indents: off;
+# kate: indent-mode python; remove-trailing-spaces modified;
diff --git a/lib/libcxx/utils/google-benchmark/tools/compare_bench.py b/lib/libcxx/utils/google-benchmark/tools/compare_bench.py
index d54baaa0e8f..7bbf0d01574 100644
--- a/lib/libcxx/utils/google-benchmark/tools/compare_bench.py
+++ b/lib/libcxx/utils/google-benchmark/tools/compare_bench.py
@@ -39,21 +39,20 @@ def main():
parser.add_argument(
'test2', metavar='test2', type=str, nargs=1,
help='A benchmark executable or JSON output file')
- # FIXME this is a dummy argument which will never actually match
- # any --benchmark flags but it helps generate a better usage message
parser.add_argument(
- 'benchmark_options', metavar='benchmark_option', nargs='*',
+ 'benchmark_options', metavar='benchmark_options', nargs=argparse.REMAINDER,
help='Arguments to pass when running benchmark executables'
)
args, unknown_args = parser.parse_known_args()
# Parse the command line flags
test1 = args.test1[0]
test2 = args.test2[0]
- if args.benchmark_options:
+ if unknown_args:
+ # should never happen
print("Unrecognized positional argument arguments: '%s'"
- % args.benchmark_options)
+ % unknown_args)
exit(1)
- benchmark_options = unknown_args
+ benchmark_options = args.benchmark_options
check_inputs(test1, test2, benchmark_options)
# Run the benchmarks and report the results
json1 = gbench.util.run_or_load_benchmark(test1, benchmark_options)
diff --git a/lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test1_run1.json b/lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test1_run1.json
index 37faed46d13..d7ec6a9c8f6 100644
--- a/lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test1_run1.json
+++ b/lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test1_run1.json
@@ -29,6 +29,20 @@
"time_unit": "ns"
},
{
+ "name": "BM_1PercentFaster",
+ "iterations": 1000,
+ "real_time": 100,
+ "cpu_time": 100,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_1PercentSlower",
+ "iterations": 1000,
+ "real_time": 100,
+ "cpu_time": 100,
+ "time_unit": "ns"
+ },
+ {
"name": "BM_10PercentFaster",
"iterations": 1000,
"real_time": 100,
@@ -55,6 +69,34 @@
"real_time": 10000,
"cpu_time": 10000,
"time_unit": "ns"
+ },
+ {
+ "name": "BM_10PercentCPUToTime",
+ "iterations": 1000,
+ "real_time": 100,
+ "cpu_time": 100,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_ThirdFaster",
+ "iterations": 1000,
+ "real_time": 100,
+ "cpu_time": 100,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_BadTimeUnit",
+ "iterations": 1000,
+ "real_time": 0.4,
+ "cpu_time": 0.5,
+ "time_unit": "s"
+ },
+ {
+ "name": "BM_DifferentTimeUnit",
+ "iterations": 1,
+ "real_time": 1,
+ "cpu_time": 1,
+ "time_unit": "s"
}
]
-} \ No newline at end of file
+}
diff --git a/lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test1_run2.json b/lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test1_run2.json
index aed5151d392..59a5ffaca4d 100644
--- a/lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test1_run2.json
+++ b/lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test1_run2.json
@@ -29,6 +29,20 @@
"time_unit": "ns"
},
{
+ "name": "BM_1PercentFaster",
+ "iterations": 1000,
+ "real_time": 98.9999999,
+ "cpu_time": 98.9999999,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_1PercentSlower",
+ "iterations": 1000,
+ "real_time": 100.9999999,
+ "cpu_time": 100.9999999,
+ "time_unit": "ns"
+ },
+ {
"name": "BM_10PercentFaster",
"iterations": 1000,
"real_time": 90,
@@ -45,8 +59,8 @@
{
"name": "BM_100xSlower",
"iterations": 1000,
- "real_time": 10000,
- "cpu_time": 10000,
+ "real_time": 1.0000e+04,
+ "cpu_time": 1.0000e+04,
"time_unit": "ns"
},
{
@@ -55,6 +69,34 @@
"real_time": 100,
"cpu_time": 100,
"time_unit": "ns"
+ },
+ {
+ "name": "BM_10PercentCPUToTime",
+ "iterations": 1000,
+ "real_time": 110,
+ "cpu_time": 90,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_ThirdFaster",
+ "iterations": 1000,
+ "real_time": 66.665,
+ "cpu_time": 66.664,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_BadTimeUnit",
+ "iterations": 1000,
+ "real_time": 0.04,
+ "cpu_time": 0.6,
+ "time_unit": "s"
+ },
+ {
+ "name": "BM_DifferentTimeUnit",
+ "iterations": 1,
+ "real_time": 1,
+ "cpu_time": 1,
+ "time_unit": "ns"
}
]
-} \ No newline at end of file
+}
diff --git a/lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test2_run.json b/lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test2_run.json
new file mode 100644
index 00000000000..15bc6980304
--- /dev/null
+++ b/lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test2_run.json
@@ -0,0 +1,81 @@
+{
+ "context": {
+ "date": "2016-08-02 17:44:46",
+ "num_cpus": 4,
+ "mhz_per_cpu": 4228,
+ "cpu_scaling_enabled": false,
+ "library_build_type": "release"
+ },
+ "benchmarks": [
+ {
+ "name": "BM_Hi",
+ "iterations": 1234,
+ "real_time": 42,
+ "cpu_time": 24,
+ "time_unit": "ms"
+ },
+ {
+ "name": "BM_Zero",
+ "iterations": 1000,
+ "real_time": 10,
+ "cpu_time": 10,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_Zero/4",
+ "iterations": 4000,
+ "real_time": 40,
+ "cpu_time": 40,
+ "time_unit": "ns"
+ },
+ {
+ "name": "Prefix/BM_Zero",
+ "iterations": 2000,
+ "real_time": 20,
+ "cpu_time": 20,
+ "time_unit": "ns"
+ },
+ {
+ "name": "Prefix/BM_Zero/3",
+ "iterations": 3000,
+ "real_time": 30,
+ "cpu_time": 30,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_One",
+ "iterations": 5000,
+ "real_time": 5,
+ "cpu_time": 5,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_One/4",
+ "iterations": 2000,
+ "real_time": 20,
+ "cpu_time": 20,
+ "time_unit": "ns"
+ },
+ {
+ "name": "Prefix/BM_One",
+ "iterations": 1000,
+ "real_time": 10,
+ "cpu_time": 10,
+ "time_unit": "ns"
+ },
+ {
+ "name": "Prefix/BM_One/3",
+ "iterations": 1500,
+ "real_time": 15,
+ "cpu_time": 15,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_Bye",
+ "iterations": 5321,
+ "real_time": 11,
+ "cpu_time": 63,
+ "time_unit": "ns"
+ }
+ ]
+}
diff --git a/lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test3_run0.json b/lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test3_run0.json
new file mode 100644
index 00000000000..ca793f3367e
--- /dev/null
+++ b/lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test3_run0.json
@@ -0,0 +1,39 @@
+{
+ "context": {
+ "date": "2016-08-02 17:44:46",
+ "num_cpus": 4,
+ "mhz_per_cpu": 4228,
+ "cpu_scaling_enabled": false,
+ "library_build_type": "release"
+ },
+ "benchmarks": [
+ {
+ "name": "BM_One",
+ "iterations": 1000,
+ "real_time": 10,
+ "cpu_time": 100,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_Two",
+ "iterations": 1000,
+ "real_time": 9,
+ "cpu_time": 90,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_Two",
+ "iterations": 1000,
+ "real_time": 8,
+ "cpu_time": 80,
+ "time_unit": "ns"
+ },
+ {
+ "name": "short",
+ "iterations": 1000,
+ "real_time": 8,
+ "cpu_time": 80,
+ "time_unit": "ns"
+ }
+ ]
+}
diff --git a/lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test3_run1.json b/lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test3_run1.json
new file mode 100644
index 00000000000..e5cf50c7445
--- /dev/null
+++ b/lib/libcxx/utils/google-benchmark/tools/gbench/Inputs/test3_run1.json
@@ -0,0 +1,39 @@
+{
+ "context": {
+ "date": "2016-08-02 17:44:46",
+ "num_cpus": 4,
+ "mhz_per_cpu": 4228,
+ "cpu_scaling_enabled": false,
+ "library_build_type": "release"
+ },
+ "benchmarks": [
+ {
+ "name": "BM_One",
+ "iterations": 1000,
+ "real_time": 9,
+ "cpu_time": 110,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_Two",
+ "iterations": 1000,
+ "real_time": 10,
+ "cpu_time": 89,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_Two",
+ "iterations": 1000,
+ "real_time": 7,
+ "cpu_time": 70,
+ "time_unit": "ns"
+ },
+ {
+ "name": "short",
+ "iterations": 1000,
+ "real_time": 8,
+ "cpu_time": 80,
+ "time_unit": "ns"
+ }
+ ]
+}
diff --git a/lib/libcxx/utils/google-benchmark/tools/gbench/report.py b/lib/libcxx/utils/google-benchmark/tools/gbench/report.py
index 8f1b0fa8604..4d03a547677 100644
--- a/lib/libcxx/utils/google-benchmark/tools/gbench/report.py
+++ b/lib/libcxx/utils/google-benchmark/tools/gbench/report.py
@@ -1,6 +1,11 @@
"""report.py - Utilities for reporting statistics about benchmark results
"""
import os
+import re
+import copy
+
+from scipy.stats import mannwhitneyu
+
class BenchmarkColor(object):
def __init__(self, name, code):
@@ -14,11 +19,13 @@ class BenchmarkColor(object):
def __format__(self, format):
return self.code
+
# Benchmark Colors Enumeration
BC_NONE = BenchmarkColor('NONE', '')
BC_MAGENTA = BenchmarkColor('MAGENTA', '\033[95m')
BC_CYAN = BenchmarkColor('CYAN', '\033[96m')
BC_OKBLUE = BenchmarkColor('OKBLUE', '\033[94m')
+BC_OKGREEN = BenchmarkColor('OKGREEN', '\033[32m')
BC_HEADER = BenchmarkColor('HEADER', '\033[92m')
BC_WARNING = BenchmarkColor('WARNING', '\033[93m')
BC_WHITE = BenchmarkColor('WHITE', '\033[97m')
@@ -27,6 +34,10 @@ BC_ENDC = BenchmarkColor('ENDC', '\033[0m')
BC_BOLD = BenchmarkColor('BOLD', '\033[1m')
BC_UNDERLINE = BenchmarkColor('UNDERLINE', '\033[4m')
+UTEST_MIN_REPETITIONS = 2
+UTEST_OPTIMAL_REPETITIONS = 9 # Lowest reasonable number, More is better.
+
+
def color_format(use_color, fmt_str, *args, **kwargs):
"""
Return the result of 'fmt_str.format(*args, **kwargs)' after transforming
@@ -66,25 +77,107 @@ def calculate_change(old_val, new_val):
return float(new_val - old_val) / abs(old_val)
-def generate_difference_report(json1, json2, use_color=True):
+def filter_benchmark(json_orig, family, replacement=""):
+ """
+ Apply a filter to the json, and only leave the 'family' of benchmarks.
+ """
+ regex = re.compile(family)
+ filtered = {}
+ filtered['benchmarks'] = []
+ for be in json_orig['benchmarks']:
+ if not regex.search(be['name']):
+ continue
+ filteredbench = copy.deepcopy(be) # Do NOT modify the old name!
+ filteredbench['name'] = regex.sub(replacement, filteredbench['name'])
+ filtered['benchmarks'].append(filteredbench)
+ return filtered
+
+
+def generate_difference_report(
+ json1,
+ json2,
+ utest=False,
+ utest_alpha=0.05,
+ use_color=True):
"""
Calculate and report the difference between each test of two benchmarks
runs specified as 'json1' and 'json2'.
"""
- first_col_width = find_longest_name(json1['benchmarks']) + 5
+ assert utest is True or utest is False
+ first_col_width = find_longest_name(json1['benchmarks'])
+
def find_test(name):
for b in json2['benchmarks']:
if b['name'] == name:
return b
return None
- first_line = "{:<{}s} Time CPU Old New".format(
- 'Benchmark', first_col_width)
+
+ utest_col_name = "_pvalue"
+ first_col_width = max(
+ first_col_width,
+ len('Benchmark'))
+ first_col_width += len(utest_col_name)
+ first_line = "{:<{}s}Time CPU Time Old Time New CPU Old CPU New".format(
+ 'Benchmark', 12 + first_col_width)
output_strs = [first_line, '-' * len(first_line)]
- for bn in json1['benchmarks']:
+
+ last_name = None
+ timings_time = [[], []]
+ timings_cpu = [[], []]
+
+ gen = (bn for bn in json1['benchmarks']
+ if 'real_time' in bn and 'cpu_time' in bn)
+ for bn in gen:
+ fmt_str = "{}{:<{}s}{endc}{}{:+16.4f}{endc}{}{:+16.4f}{endc}{:14.0f}{:14.0f}{endc}{:14.0f}{:14.0f}"
+ special_str = "{}{:<{}s}{endc}{}{:16.4f}{endc}{}{:16.4f}{endc}{} {}"
+
+ if last_name is None:
+ last_name = bn['name']
+ if last_name != bn['name']:
+ if ((len(timings_time[0]) >= UTEST_MIN_REPETITIONS) and
+ (len(timings_time[1]) >= UTEST_MIN_REPETITIONS) and
+ (len(timings_cpu[0]) >= UTEST_MIN_REPETITIONS) and
+ (len(timings_cpu[1]) >= UTEST_MIN_REPETITIONS)):
+ if utest:
+ def get_utest_color(pval):
+ if pval >= utest_alpha:
+ return BC_FAIL
+ else:
+ return BC_OKGREEN
+ time_pvalue = mannwhitneyu(
+ timings_time[0], timings_time[1], alternative='two-sided').pvalue
+ cpu_pvalue = mannwhitneyu(
+ timings_cpu[0], timings_cpu[1], alternative='two-sided').pvalue
+ dsc = "U Test, Repetitions: {}".format(len(timings_cpu[0]))
+ dsc_color = BC_OKGREEN
+ if len(timings_cpu[0]) < UTEST_OPTIMAL_REPETITIONS:
+ dsc_color = BC_WARNING
+ dsc += ". WARNING: Results unreliable! {}+ repetitions recommended.".format(
+ UTEST_OPTIMAL_REPETITIONS)
+ output_strs += [color_format(use_color,
+ special_str,
+ BC_HEADER,
+ "{}{}".format(last_name,
+ utest_col_name),
+ first_col_width,
+ get_utest_color(time_pvalue),
+ time_pvalue,
+ get_utest_color(cpu_pvalue),
+ cpu_pvalue,
+ dsc_color,
+ dsc,
+ endc=BC_ENDC)]
+ last_name = bn['name']
+ timings_time = [[], []]
+ timings_cpu = [[], []]
+
other_bench = find_test(bn['name'])
if not other_bench:
continue
+ if bn['time_unit'] != other_bench['time_unit']:
+ continue
+
def get_color(res):
if res > 0.05:
return BC_FAIL
@@ -92,25 +185,44 @@ def generate_difference_report(json1, json2, use_color=True):
return BC_WHITE
else:
return BC_CYAN
- fmt_str = "{}{:<{}s}{endc}{}{:+9.2f}{endc}{}{:+14.2f}{endc}{:14d}{:14d}"
- tres = calculate_change(bn['real_time'], other_bench['real_time'])
- cpures = calculate_change(bn['cpu_time'], other_bench['cpu_time'])
- output_strs += [color_format(use_color, fmt_str,
- BC_HEADER, bn['name'], first_col_width,
- get_color(tres), tres, get_color(cpures), cpures,
- bn['cpu_time'], other_bench['cpu_time'],
- endc=BC_ENDC)]
+
+ timings_time[0].append(bn['real_time'])
+ timings_time[1].append(other_bench['real_time'])
+ timings_cpu[0].append(bn['cpu_time'])
+ timings_cpu[1].append(other_bench['cpu_time'])
+
+ tres = calculate_change(timings_time[0][-1], timings_time[1][-1])
+ cpures = calculate_change(timings_cpu[0][-1], timings_cpu[1][-1])
+ output_strs += [color_format(use_color,
+ fmt_str,
+ BC_HEADER,
+ bn['name'],
+ first_col_width,
+ get_color(tres),
+ tres,
+ get_color(cpures),
+ cpures,
+ timings_time[0][-1],
+ timings_time[1][-1],
+ timings_cpu[0][-1],
+ timings_cpu[1][-1],
+ endc=BC_ENDC)]
return output_strs
###############################################################################
# Unit tests
+
import unittest
+
class TestReportDifference(unittest.TestCase):
def load_results(self):
import json
- testInputs = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'Inputs')
+ testInputs = os.path.join(
+ os.path.dirname(
+ os.path.realpath(__file__)),
+ 'Inputs')
testOutput1 = os.path.join(testInputs, 'test1_run1.json')
testOutput2 = os.path.join(testInputs, 'test1_run2.json')
with open(testOutput1, 'r') as f:
@@ -121,24 +233,120 @@ class TestReportDifference(unittest.TestCase):
def test_basic(self):
expect_lines = [
- ['BM_SameTimes', '+0.00', '+0.00', '10', '10'],
- ['BM_2xFaster', '-0.50', '-0.50', '50', '25'],
- ['BM_2xSlower', '+1.00', '+1.00', '50', '100'],
- ['BM_10PercentFaster', '-0.10', '-0.10', '100', '90'],
- ['BM_10PercentSlower', '+0.10', '+0.10', '100', '110'],
- ['BM_100xSlower', '+99.00', '+99.00', '100', '10000'],
- ['BM_100xFaster', '-0.99', '-0.99', '10000', '100'],
+ ['BM_SameTimes', '+0.0000', '+0.0000', '10', '10', '10', '10'],
+ ['BM_2xFaster', '-0.5000', '-0.5000', '50', '25', '50', '25'],
+ ['BM_2xSlower', '+1.0000', '+1.0000', '50', '100', '50', '100'],
+ ['BM_1PercentFaster', '-0.0100', '-0.0100', '100', '99', '100', '99'],
+ ['BM_1PercentSlower', '+0.0100', '+0.0100', '100', '101', '100', '101'],
+ ['BM_10PercentFaster', '-0.1000', '-0.1000', '100', '90', '100', '90'],
+ ['BM_10PercentSlower', '+0.1000', '+0.1000', '100', '110', '100', '110'],
+ ['BM_100xSlower', '+99.0000', '+99.0000',
+ '100', '10000', '100', '10000'],
+ ['BM_100xFaster', '-0.9900', '-0.9900',
+ '10000', '100', '10000', '100'],
+ ['BM_10PercentCPUToTime', '+0.1000',
+ '-0.1000', '100', '110', '100', '90'],
+ ['BM_ThirdFaster', '-0.3333', '-0.3334', '100', '67', '100', '67'],
+ ['BM_BadTimeUnit', '-0.9000', '+0.2000', '0', '0', '0', '1'],
]
json1, json2 = self.load_results()
- output_lines_with_header = generate_difference_report(json1, json2, use_color=False)
+ output_lines_with_header = generate_difference_report(
+ json1, json2, use_color=False)
output_lines = output_lines_with_header[2:]
+ print("\n")
print("\n".join(output_lines_with_header))
self.assertEqual(len(output_lines), len(expect_lines))
- for i in xrange(0, len(output_lines)):
+ for i in range(0, len(output_lines)):
+ parts = [x for x in output_lines[i].split(' ') if x]
+ self.assertEqual(len(parts), 7)
+ self.assertEqual(parts, expect_lines[i])
+
+
+class TestReportDifferenceBetweenFamilies(unittest.TestCase):
+ def load_result(self):
+ import json
+ testInputs = os.path.join(
+ os.path.dirname(
+ os.path.realpath(__file__)),
+ 'Inputs')
+ testOutput = os.path.join(testInputs, 'test2_run.json')
+ with open(testOutput, 'r') as f:
+ json = json.load(f)
+ return json
+
+ def test_basic(self):
+ expect_lines = [
+ ['.', '-0.5000', '-0.5000', '10', '5', '10', '5'],
+ ['./4', '-0.5000', '-0.5000', '40', '20', '40', '20'],
+ ['Prefix/.', '-0.5000', '-0.5000', '20', '10', '20', '10'],
+ ['Prefix/./3', '-0.5000', '-0.5000', '30', '15', '30', '15'],
+ ]
+ json = self.load_result()
+ json1 = filter_benchmark(json, "BM_Z.ro", ".")
+ json2 = filter_benchmark(json, "BM_O.e", ".")
+ output_lines_with_header = generate_difference_report(
+ json1, json2, use_color=False)
+ output_lines = output_lines_with_header[2:]
+ print("\n")
+ print("\n".join(output_lines_with_header))
+ self.assertEqual(len(output_lines), len(expect_lines))
+ for i in range(0, len(output_lines)):
+ parts = [x for x in output_lines[i].split(' ') if x]
+ self.assertEqual(len(parts), 7)
+ self.assertEqual(parts, expect_lines[i])
+
+
+class TestReportDifferenceWithUTest(unittest.TestCase):
+ def load_results(self):
+ import json
+ testInputs = os.path.join(
+ os.path.dirname(
+ os.path.realpath(__file__)),
+ 'Inputs')
+ testOutput1 = os.path.join(testInputs, 'test3_run0.json')
+ testOutput2 = os.path.join(testInputs, 'test3_run1.json')
+ with open(testOutput1, 'r') as f:
+ json1 = json.load(f)
+ with open(testOutput2, 'r') as f:
+ json2 = json.load(f)
+ return json1, json2
+
+ def test_utest(self):
+ expect_lines = []
+ expect_lines = [
+ ['BM_One', '-0.1000', '+0.1000', '10', '9', '100', '110'],
+ ['BM_Two', '+0.1111', '-0.0111', '9', '10', '90', '89'],
+ ['BM_Two', '+0.2500', '+0.1125', '8', '10', '80', '89'],
+ ['BM_Two_pvalue',
+ '0.2207',
+ '0.6831',
+ 'U',
+ 'Test,',
+ 'Repetitions:',
+ '2.',
+ 'WARNING:',
+ 'Results',
+ 'unreliable!',
+ '9+',
+ 'repetitions',
+ 'recommended.'],
+ ['short', '+0.0000', '+0.0000', '8', '8', '80', '80'],
+ ]
+ json1, json2 = self.load_results()
+ output_lines_with_header = generate_difference_report(
+ json1, json2, True, 0.05, use_color=False)
+ output_lines = output_lines_with_header[2:]
+ print("\n")
+ print("\n".join(output_lines_with_header))
+ self.assertEqual(len(output_lines), len(expect_lines))
+ for i in range(0, len(output_lines)):
parts = [x for x in output_lines[i].split(' ') if x]
- self.assertEqual(len(parts), 5)
self.assertEqual(parts, expect_lines[i])
if __name__ == '__main__':
unittest.main()
+
+# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
+# kate: tab-width: 4; replace-tabs on; indent-width 4; tab-indents: off;
+# kate: indent-mode python; remove-trailing-spaces modified;
diff --git a/lib/libcxx/utils/google-benchmark/tools/strip_asm.py b/lib/libcxx/utils/google-benchmark/tools/strip_asm.py
new file mode 100755
index 00000000000..9030550b43b
--- /dev/null
+++ b/lib/libcxx/utils/google-benchmark/tools/strip_asm.py
@@ -0,0 +1,151 @@
+#!/usr/bin/env python
+
+"""
+strip_asm.py - Cleanup ASM output for the specified file
+"""
+
+from argparse import ArgumentParser
+import sys
+import os
+import re
+
+def find_used_labels(asm):
+ found = set()
+ label_re = re.compile("\s*j[a-z]+\s+\.L([a-zA-Z0-9][a-zA-Z0-9_]*)")
+ for l in asm.splitlines():
+ m = label_re.match(l)
+ if m:
+ found.add('.L%s' % m.group(1))
+ return found
+
+
+def normalize_labels(asm):
+ decls = set()
+ label_decl = re.compile("^[.]{0,1}L([a-zA-Z0-9][a-zA-Z0-9_]*)(?=:)")
+ for l in asm.splitlines():
+ m = label_decl.match(l)
+ if m:
+ decls.add(m.group(0))
+ if len(decls) == 0:
+ return asm
+ needs_dot = next(iter(decls))[0] != '.'
+ if not needs_dot:
+ return asm
+ for ld in decls:
+ asm = re.sub("(^|\s+)" + ld + "(?=:|\s)", '\\1.' + ld, asm)
+ return asm
+
+
+def transform_labels(asm):
+ asm = normalize_labels(asm)
+ used_decls = find_used_labels(asm)
+ new_asm = ''
+ label_decl = re.compile("^\.L([a-zA-Z0-9][a-zA-Z0-9_]*)(?=:)")
+ for l in asm.splitlines():
+ m = label_decl.match(l)
+ if not m or m.group(0) in used_decls:
+ new_asm += l
+ new_asm += '\n'
+ return new_asm
+
+
+def is_identifier(tk):
+ if len(tk) == 0:
+ return False
+ first = tk[0]
+ if not first.isalpha() and first != '_':
+ return False
+ for i in range(1, len(tk)):
+ c = tk[i]
+ if not c.isalnum() and c != '_':
+ return False
+ return True
+
+def process_identifiers(l):
+ """
+ process_identifiers - process all identifiers and modify them to have
+ consistent names across all platforms; specifically across ELF and MachO.
+ For example, MachO inserts an additional understore at the beginning of
+ names. This function removes that.
+ """
+ parts = re.split(r'([a-zA-Z0-9_]+)', l)
+ new_line = ''
+ for tk in parts:
+ if is_identifier(tk):
+ if tk.startswith('__Z'):
+ tk = tk[1:]
+ elif tk.startswith('_') and len(tk) > 1 and \
+ tk[1].isalpha() and tk[1] != 'Z':
+ tk = tk[1:]
+ new_line += tk
+ return new_line
+
+
+def process_asm(asm):
+ """
+ Strip the ASM of unwanted directives and lines
+ """
+ new_contents = ''
+ asm = transform_labels(asm)
+
+ # TODO: Add more things we want to remove
+ discard_regexes = [
+ re.compile("\s+\..*$"), # directive
+ re.compile("\s*#(NO_APP|APP)$"), #inline ASM
+ re.compile("\s*#.*$"), # comment line
+ re.compile("\s*\.globa?l\s*([.a-zA-Z_][a-zA-Z0-9$_.]*)"), #global directive
+ re.compile("\s*\.(string|asciz|ascii|[1248]?byte|short|word|long|quad|value|zero)"),
+ ]
+ keep_regexes = [
+
+ ]
+ fn_label_def = re.compile("^[a-zA-Z_][a-zA-Z0-9_.]*:")
+ for l in asm.splitlines():
+ # Remove Mach-O attribute
+ l = l.replace('@GOTPCREL', '')
+ add_line = True
+ for reg in discard_regexes:
+ if reg.match(l) is not None:
+ add_line = False
+ break
+ for reg in keep_regexes:
+ if reg.match(l) is not None:
+ add_line = True
+ break
+ if add_line:
+ if fn_label_def.match(l) and len(new_contents) != 0:
+ new_contents += '\n'
+ l = process_identifiers(l)
+ new_contents += l
+ new_contents += '\n'
+ return new_contents
+
+def main():
+ parser = ArgumentParser(
+ description='generate a stripped assembly file')
+ parser.add_argument(
+ 'input', metavar='input', type=str, nargs=1,
+ help='An input assembly file')
+ parser.add_argument(
+ 'out', metavar='output', type=str, nargs=1,
+ help='The output file')
+ args, unknown_args = parser.parse_known_args()
+ input = args.input[0]
+ output = args.out[0]
+ if not os.path.isfile(input):
+ print(("ERROR: input file '%s' does not exist") % input)
+ sys.exit(1)
+ contents = None
+ with open(input, 'r') as f:
+ contents = f.read()
+ new_contents = process_asm(contents)
+ with open(output, 'w') as f:
+ f.write(new_contents)
+
+
+if __name__ == '__main__':
+ main()
+
+# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
+# kate: tab-width: 4; replace-tabs on; indent-width 4; tab-indents: off;
+# kate: indent-mode python; remove-trailing-spaces modified;