1 """report.py - Utilities for reporting statistics about benchmark results
7 class BenchmarkColor(object):
8 def __init__(self
, name
, code
):
13 return '%s%r' % (self
.__class
__.__name
__,
14 (self
.name
, self
.code
))
16 def __format__(self
, format
):
19 # Benchmark Colors Enumeration
20 BC_NONE
= BenchmarkColor('NONE', '')
21 BC_MAGENTA
= BenchmarkColor('MAGENTA', '\033[95m')
22 BC_CYAN
= BenchmarkColor('CYAN', '\033[96m')
23 BC_OKBLUE
= BenchmarkColor('OKBLUE', '\033[94m')
24 BC_HEADER
= BenchmarkColor('HEADER', '\033[92m')
25 BC_WARNING
= BenchmarkColor('WARNING', '\033[93m')
26 BC_WHITE
= BenchmarkColor('WHITE', '\033[97m')
27 BC_FAIL
= BenchmarkColor('FAIL', '\033[91m')
28 BC_ENDC
= BenchmarkColor('ENDC', '\033[0m')
29 BC_BOLD
= BenchmarkColor('BOLD', '\033[1m')
30 BC_UNDERLINE
= BenchmarkColor('UNDERLINE', '\033[4m')
32 def color_format(use_color
, fmt_str
, *args
, **kwargs
):
34 Return the result of 'fmt_str.format(*args, **kwargs)' after transforming
35 'args' and 'kwargs' according to the value of 'use_color'. If 'use_color'
36 is False then all color codes in 'args' and 'kwargs' are replaced with
39 assert use_color
is True or use_color
is False
41 args
= [arg
if not isinstance(arg
, BenchmarkColor
) else BC_NONE
43 kwargs
= {key
: arg
if not isinstance(arg
, BenchmarkColor
) else BC_NONE
44 for key
, arg
in kwargs
.items()}
45 return fmt_str
.format(*args
, **kwargs
)
48 def find_longest_name(benchmark_list
):
50 Return the length of the longest benchmark name in a given list of
51 benchmark JSON objects
54 for bc
in benchmark_list
:
55 if len(bc
['name']) > longest_name
:
56 longest_name
= len(bc
['name'])
60 def calculate_change(old_val
, new_val
):
62 Return a float representing the decimal change between old_val and new_val.
64 if old_val
== 0 and new_val
== 0:
67 return float(new_val
- old_val
) / (float(old_val
+ new_val
) / 2)
68 return float(new_val
- old_val
) / abs(old_val
)
71 def filter_benchmark(json_orig
, family
, replacement
=""):
73 Apply a filter to the json, and only leave the 'family' of benchmarks.
75 regex
= re
.compile(family
)
77 filtered
['benchmarks'] = []
78 for be
in json_orig
['benchmarks']:
79 if not regex
.search(be
['name']):
81 filteredbench
= copy
.deepcopy(be
) # Do NOT modify the old name!
82 filteredbench
['name'] = regex
.sub(replacement
, filteredbench
['name'])
83 filtered
['benchmarks'].append(filteredbench
)
87 def generate_difference_report(json1
, json2
, use_color
=True):
89 Calculate and report the difference between each test of two benchmarks
90 runs specified as 'json1' and 'json2'.
92 first_col_width
= find_longest_name(json1
['benchmarks'])
94 for b
in json2
['benchmarks']:
98 first_col_width
= max(first_col_width
, len('Benchmark'))
99 first_line
= "{:<{}s}Time CPU Time Old Time New CPU Old CPU New".format(
100 'Benchmark', 12 + first_col_width
)
101 output_strs
= [first_line
, '-' * len(first_line
)]
103 gen
= (bn
for bn
in json1
['benchmarks'] if 'real_time' in bn
and 'cpu_time' in bn
)
105 other_bench
= find_test(bn
['name'])
109 if bn
['time_unit'] != other_bench
['time_unit']:
119 fmt_str
= "{}{:<{}s}{endc}{}{:+16.4f}{endc}{}{:+16.4f}{endc}{:14.0f}{:14.0f}{endc}{:14.0f}{:14.0f}"
120 tres
= calculate_change(bn
['real_time'], other_bench
['real_time'])
121 cpures
= calculate_change(bn
['cpu_time'], other_bench
['cpu_time'])
122 output_strs
+= [color_format(use_color
, fmt_str
,
123 BC_HEADER
, bn
['name'], first_col_width
,
124 get_color(tres
), tres
, get_color(cpures
), cpures
,
125 bn
['real_time'], other_bench
['real_time'],
126 bn
['cpu_time'], other_bench
['cpu_time'],
130 ###############################################################################
135 class TestReportDifference(unittest
.TestCase
):
136 def load_results(self
):
138 testInputs
= os
.path
.join(os
.path
.dirname(os
.path
.realpath(__file__
)), 'Inputs')
139 testOutput1
= os
.path
.join(testInputs
, 'test1_run1.json')
140 testOutput2
= os
.path
.join(testInputs
, 'test1_run2.json')
141 with
open(testOutput1
, 'r') as f
:
143 with
open(testOutput2
, 'r') as f
:
147 def test_basic(self
):
149 ['BM_SameTimes', '+0.0000', '+0.0000', '10', '10', '10', '10'],
150 ['BM_2xFaster', '-0.5000', '-0.5000', '50', '25', '50', '25'],
151 ['BM_2xSlower', '+1.0000', '+1.0000', '50', '100', '50', '100'],
152 ['BM_1PercentFaster', '-0.0100', '-0.0100', '100', '99', '100', '99'],
153 ['BM_1PercentSlower', '+0.0100', '+0.0100', '100', '101', '100', '101'],
154 ['BM_10PercentFaster', '-0.1000', '-0.1000', '100', '90', '100', '90'],
155 ['BM_10PercentSlower', '+0.1000', '+0.1000', '100', '110', '100', '110'],
156 ['BM_100xSlower', '+99.0000', '+99.0000', '100', '10000', '100', '10000'],
157 ['BM_100xFaster', '-0.9900', '-0.9900', '10000', '100', '10000', '100'],
158 ['BM_10PercentCPUToTime', '+0.1000', '-0.1000', '100', '110', '100', '90'],
159 ['BM_ThirdFaster', '-0.3333', '-0.3334', '100', '67', '100', '67'],
160 ['BM_BadTimeUnit', '-0.9000', '+0.2000', '0', '0', '0', '1'],
162 json1
, json2
= self
.load_results()
163 output_lines_with_header
= generate_difference_report(json1
, json2
, use_color
=False)
164 output_lines
= output_lines_with_header
[2:]
165 print("\n".join(output_lines_with_header
))
166 self
.assertEqual(len(output_lines
), len(expect_lines
))
167 for i
in range(0, len(output_lines
)):
168 parts
= [x
for x
in output_lines
[i
].split(' ') if x
]
169 self
.assertEqual(len(parts
), 7)
170 self
.assertEqual(parts
, expect_lines
[i
])
173 class TestReportDifferenceBetweenFamilies(unittest
.TestCase
):
174 def load_result(self
):
176 testInputs
= os
.path
.join(os
.path
.dirname(os
.path
.realpath(__file__
)), 'Inputs')
177 testOutput
= os
.path
.join(testInputs
, 'test2_run.json')
178 with
open(testOutput
, 'r') as f
:
182 def test_basic(self
):
184 ['.', '-0.5000', '-0.5000', '10', '5', '10', '5'],
185 ['./4', '-0.5000', '-0.5000', '40', '20', '40', '20'],
186 ['Prefix/.', '-0.5000', '-0.5000', '20', '10', '20', '10'],
187 ['Prefix/./3', '-0.5000', '-0.5000', '30', '15', '30', '15'],
189 json
= self
.load_result()
190 json1
= filter_benchmark(json
, "BM_Z.ro", ".")
191 json2
= filter_benchmark(json
, "BM_O.e", ".")
192 output_lines_with_header
= generate_difference_report(json1
, json2
, use_color
=False)
193 output_lines
= output_lines_with_header
[2:]
195 print("\n".join(output_lines_with_header
))
196 self
.assertEqual(len(output_lines
), len(expect_lines
))
197 for i
in range(0, len(output_lines
)):
198 parts
= [x
for x
in output_lines
[i
].split(' ') if x
]
199 self
.assertEqual(len(parts
), 7)
200 self
.assertEqual(parts
, expect_lines
[i
])
203 if __name__
== '__main__':
206 # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
207 # kate: tab-width: 4; replace-tabs on; indent-width 4; tab-indents: off;
208 # kate: indent-mode python; remove-trailing-spaces modified;