1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:convert' show json;
import 'dart:math' as math;
double _doNormal(
{required double mean, required double stddev, required double x}) {
return (1.0 / (stddev * math.sqrt(2.0 * math.pi))) *
math.pow(math.e, -0.5 * math.pow((x - mean) / stddev, 2.0));
}
double _doMean(List<double> values) =>
values.reduce((double x, double y) => x + y) / values.length;
double _doStddev(List<double> values, double mean) {
double stddev = 0.0;
for (final double value in values) {
stddev += (value - mean) * (value - mean);
}
return math.sqrt(stddev / values.length);
}
double _doIntegral({
required double Function(double) func,
required double start,
required double stop,
required double resolution,
}) {
double result = 0.0;
while (start < stop) {
final double value = func(start);
result += resolution * value;
start += resolution;
}
return result;
}
/// Probability is defined as the probability that the mean is within the
/// [margin] of the true value.
double _doProbability({required double mean, required double stddev, required double margin}) {
return _doIntegral(
func: (double x) => _doNormal(mean: mean, stddev: stddev, x: x),
start: (1.0 - margin) * mean,
stop: (1.0 + margin) * mean,
resolution: 0.001,
);
}
/// This class knows how to format benchmark results for machine and human
/// consumption.
///
/// Example:
///
/// BenchmarkResultPrinter printer = new BenchmarkResultPrinter();
/// printer.add(
/// description: 'Average frame time',
/// value: averageFrameTime,
/// unit: 'ms',
/// name: 'average_frame_time',
/// );
/// printer.printToStdout();
///
class BenchmarkResultPrinter {
final List<_BenchmarkResult> _results = <_BenchmarkResult>[];
/// Adds a benchmark result to the list of results.
///
/// [description] is a human-readable description of the result. [value] is a
/// result value. [unit] is the unit of measurement, such as "ms", "km", "h".
/// [name] is a computer-readable name of the result used as a key in the JSON
/// serialization of the results.
void addResult({ required String description, required double value, required String unit, required String name }) {
_results.add(_BenchmarkResult(description, value, unit, name));
}
/// Adds a benchmark result to the list of results and a probability of that
/// result.
///
/// The probability is calculated as the probability that the mean is +- 5% of
/// the true value.
///
/// See also [addResult].
void addResultStatistics({
required String description,
required List<double> values,
required String unit,
required String name,
}) {
final double mean = _doMean(values);
final double stddev = _doStddev(values, mean);
const double margin = 0.05;
final double probability = _doProbability(mean: mean, stddev: stddev, margin: margin);
_results.add(_BenchmarkResult(description, mean, unit, name));
_results.add(_BenchmarkResult('$description - probability margin of error $margin', probability,
'percent', '${name}_probability_5pct'));
}
/// Prints the results added via [addResult] to standard output, once as JSON
/// for computer consumption and once formatted as plain text for humans.
void printToStdout() {
// IMPORTANT: keep these values in sync with dev/devicelab/bin/tasks/microbenchmarks.dart
const String jsonStart = '================ RESULTS ================';
const String jsonEnd = '================ FORMATTED ==============';
const String jsonPrefix = ':::JSON:::';
print(jsonStart);
print('$jsonPrefix ${_printJson()}');
print(jsonEnd);
print(_printPlainText());
}
String _printJson() {
final Map<String, double> results = <String, double>{};
for (final _BenchmarkResult result in _results) {
results[result.name] = result.value;
}
return json.encode(results);
}
String _printPlainText() {
final StringBuffer buf = StringBuffer();
for (final _BenchmarkResult result in _results) {
buf.writeln('${result.description}: ${result.value.toStringAsFixed(1)} ${result.unit}');
}
return buf.toString();
}
}
class _BenchmarkResult {
_BenchmarkResult(this.description, this.value, this.unit, this.name);
/// Human-readable description of the result, e.g. "Average frame time".
final String description;
/// Result value that in agreement with [unit].
final double value;
/// Unit of measurement that is in agreement with [value].
final String unit;
/// Computer-readable name of the result.
final String name;
}