Skip to content

Commit fd6f4ef

Browse files
committed
Removed dependency to benchmark_harness leading to a more modular class layout.
1 parent 1d4ca4a commit fd6f4ef

14 files changed

+217
-309
lines changed

CHANGELOG.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
## 2.0.0
2+
Breaking changes:
3+
- The classes `Benchmark` and `AsyncBenchmark` are now solely responsible for
4+
generating benchmark scores. The constructor parameters `description` and
5+
`emitter` have been removed. Generating reports is delegated to `ScoreEmitter`.
6+
- The functions `benchmark` and `asyncBenchmark` are not longer generic and
7+
the only optional parameter is: `scoreEmitter`. A custom `ScoreEmitter` can be
8+
used to generate a custom benchmark report.
9+
10+
111
## 1.0.0
212
Breaking changes:
313
- The command `benchmark_runner` now has subcommands `report` and `export`.
@@ -29,8 +39,9 @@ inter-quartile-range `iqr` of the score sample is zero.
2939

3040
## 0.1.5
3141
- Made [BenchmarkHelper.sampleSize][sampleSize] a variable assignable with
32-
defined function. This allows changing the benchmark runtime by customizing
33-
the relation between score estimate and score sample size.
42+
a user defined function. This allows changing the score sample generation
43+
customizing the relation between the single score estimate and the
44+
score sample size.
3445

3546
## 0.1.4
3647
- Fixed bugs in runner (results were listed twice, exit code was always 0).

README.md

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66

77
Benchmarking is used to estimate and compare the execution speed of
88
numerical algorithms and programs.
9-
The package [`benchmark_runner`][benchmark_runner] is based on
10-
[`benchmark_harness`][benchmark_harness] and includes helper
9+
The package [`benchmark_runner`][benchmark_runner] includes helper
1110
functions for writing *inline* micro-benchmarks with the option of
1211
printing a score **histogram** and reporting the score **mean** ±
1312
**standard deviation**, and score **median** ± **inter quartile range**.
@@ -36,8 +35,6 @@ Write inline benchmarks using the functions:
3635
asynchronous benchmarks.
3736

3837
```Dart
39-
// ignore_for_file: unused_local_variable
40-
4138
import 'package:benchmark_runner/benchmark_runner.dart';
4239
4340
/// Returns the value [t] after waiting for [duration].
@@ -53,7 +50,7 @@ Write inline benchmarks using the functions:
5350
5451
await asyncBenchmark('5ms', () async {
5552
await later<int>(27, Duration(milliseconds: 5));
56-
}, emitStats: false);
53+
}, scoreEmitter: MeanEmitter());
5754
});
5855
5956
group('Set', () async {
@@ -115,25 +112,27 @@ To export benchmark scores use the sub-command `export`:
115112
$ dart run benchmark_runner export --outputDir=scores --extension=csv searchDirectory
116113
```
117114
In the example above, `searchDirectory` is scanned for `*_benchmark.dart`
118-
files. For each benchmark file a corresponding file `*_benchmark.csv` is
119-
written to the directory `scores`. The directory must exist and the user
120-
must have write access.
115+
files. For each benchmark file, a corresponding file `*_benchmark.csv` is
116+
written to the directory `scores`.
121117

122-
Note: When exporting benchmark scores to a file
118+
Note: The directory must exist and the user
119+
must have write access. When exporting benchmark scores to a file
123120
and the emitter output is colorized,
124121
it is recommended to use the option `--isMonochrome`, to
125122
avoid spurious characters due to the use of Ansi modifiers.
126123

127-
Since version 1.0.0, the functions [`benchmark`][benchmark] and
128-
[`asyncBenchmark`][asyncBenchmark] accept the optional parameters `emitter` and
129-
`report`. These parameters can be used to customize the score reports e.g.
124+
The functions [`benchmark`][benchmark] and
125+
[`asyncBenchmark`][asyncBenchmark] accept the optional parameters `scoreEmitter`.
126+
The parameter expects an object of type `ScoreEmitter` and
127+
can be used to customize the score reports e.g.
130128
to make the score format more suitable for writing to a file:
131129

132130
```Dart
133131
import 'package:benchmark_runner/benchmark_runner.dart';
134132
135-
class CustomEmitter extends ColorPrintEmitter {
136-
void emitMean({required Score score}) {
133+
class CustomEmitter implements ScoreEmitter {
134+
@override
135+
void emit({required description, required Score score}) {
137136
print('# Mean Standard Deviation');
138137
print('${score.stats.mean} ${score.stats.stdDev}');
139138
}
@@ -145,11 +144,8 @@ void main(){
145144
() {
146145
var list = <int>[for (var i = 0; i < 1000; ++i) i];
147146
},
148-
emitter: CustomEmitter(),
149-
report: (instance, emitter) => emitter.emitMean(
150-
score: instance.score(),
151-
),
152-
);
147+
scoreEmitter: CustomEmitter(),
148+
);
153149
}
154150
```
155151

@@ -170,49 +166,54 @@ as reported by [`benchmark_harness`][benchmark_harness] and the
170166
score statistics.
171167

172168
- By default, [`benchmark`][benchmark] and
173-
[`asyncBenchmark`][asyncBenchmark] report score statistics. In order to generate
174-
the report provided by [`benchmark_harness`][benchmark_harness] use the
175-
optional argument `report: reportMean`.
169+
[`asyncBenchmark`][asyncBenchmark] report score statistics. In order to print
170+
the report similar to that produced by
171+
[`benchmark_harness`][benchmark_harness], use the
172+
optional argument `emitter: MeanEmitter()`.
176173

177174
- Color output can be switched off by using the option: `--isMonochrome` or `-m`
178175
when calling the benchmark runner. When executing a single benchmark file the
179176
corresponding option is `--define=isMonochrome=true`.
180177

181178
- The default colors used to style benchmark reports are best suited
182179
for a dark terminal background.
183-
They can, however, be altered by setting the static variables defined by
180+
They can, however, be altered by setting the *static* variables defined by
184181
the class [`ColorProfile`][ColorProfile]. In the example below, the styling of
185182
error messages and the mean value is altered.
186183
```Dart
187184
import 'package:ansi_modifier/ansi_modifier.dart';
188185
import 'package:benchmark_runner/benchmark_runner.dart';
189186
190-
void customColorProfile() {
187+
void adjustColorProfile() {
191188
ColorProfile.error = Ansi.red + Ansi.bold;
192189
ColorProfile.mean = Ansi.green + Ansi.italic;
193190
}
194191
195192
void main(List<String> args) {
196193
// Call function to apply the new custom color profile.
197-
customColorProfile();
194+
adjustColorProfile();
198195
}
199196
```
200197

201198
- When running **asynchronous** benchmarks, the scores are printed in order of
202-
completion. The print the scores in sequential order (as they are listed in the
199+
completion. To print the scores in sequential order (as they are listed in the
203200
benchmark executable) it is required to *await* the completion
204201
of the async benchmark functions and
205202
the enclosing group.
206203

207204
## Score Sampling
208205

209-
In order to calculate benchmark score statistics a sample of scores is
206+
In order to calculate benchmark score *statistics* a sample of scores is
210207
required. The question is how to generate the score sample while minimizing
211208
systematic errors (like overheads) and keeping the
212-
benchmark run times within acceptable limits.
209+
total benchmark run times within acceptable limits.
210+
211+
<details> <summary> Click to show details. </summary>
213212

214-
To estimate the benchmark score the functions [`warmup`][warmup]
215-
or [`warmupAsync`][warmupAsync] are run for 200 milliseconds.
213+
In a first step, benchmark scores are estimated using the
214+
functions [`warmup`][warmup]
215+
or [`warmupAsync`][warmupAsync]. The function [`BenchmarkHelper.sampleSize`][sampleSize]
216+
uses the score estimate to determine the sampling procedure.
216217

217218
### 1. Default Sampling Method
218219
The graph below shows the sample size (orange curve) as calculated by the function
@@ -234,9 +235,10 @@ averaged over (see the cyan curve in the graph above):
234235
* ticks > 1e5 => No preliminary averaging of sample scores.
235236

236237
### 2. Custom Sampling Method
237-
To amend the score sampling process the static function
238+
To custominze the score sampling process, the static function
238239
[`BenchmarkHelper.sampleSize`][sampleSize] can be replaced with a custom function:
239240
```Dart
241+
/// Generates a sample containing 100 benchmark scores.
240242
BenchmarkHelper.sampleSize = (int clockTicks) {
241243
return (outer: 100, inner: 1)
242244
}
@@ -256,6 +258,7 @@ The command above lauches a process and runs a [`gnuplot`][gnuplot] script.
256258
For this reason, the program [`gnuplot`][gnuplot] must be installed (with
257259
the `qt` terminal enabled).
258260

261+
</details>
259262

260263
## Contributions
261264

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// ignore_for_file: unused_local_variable
22
import 'package:benchmark_runner/benchmark_runner.dart';
33

4-
class CustomEmitter extends ColorPrintEmitter {
5-
void emitMean({required Score score}) {
4+
class CustomEmitter implements ScoreEmitter {
5+
@override
6+
void emit({required description, required Score score}) {
67
print('Mean Standard Deviation');
78
print('${score.stats.mean} ${score.stats.stdDev}');
89
}
@@ -14,9 +15,6 @@ void main(List<String> args) {
1415
() {
1516
var list = <int>[for (var i = 0; i < 1000; ++i) i];
1617
},
17-
emitter: CustomEmitter(),
18-
report: (instance, emitter) => emitter.emitMean(
19-
score: instance.score(),
20-
),
18+
scoreEmitter: CustomEmitter(),
2119
);
2220
}

benchmark/example_async_benchmark.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ void main(List<String> args) async {
1515

1616
await asyncBenchmark('5ms', () async {
1717
await later<int>(27, Duration(milliseconds: 5));
18-
}, report: asyncReportMean);
18+
}, scoreEmitter: MeanEmitter());
1919
});
2020

2121
group('Set', () async {

benchmark/example_benchmark.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ void main(List<String> args) {
2121

2222
benchmark('construct', () {
2323
var list = <int>[for (var i = 0; i < 1000; ++i) i];
24-
}, report: reportMean);
24+
}, scoreEmitter: MeanEmitter());
2525
});
2626
}

gnuplot/sample_size.dart

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,23 @@ import 'package:ansi_modifier/ansi_modifier.dart';
66
import 'package:benchmark_runner/benchmark_runner.dart' show BenchmarkHelper;
77

88
final ticksList = [
9+
1,
10+
2,
11+
4,
12+
6,
13+
8,
14+
10,
15+
15,
16+
25,
17+
40,
18+
70,
19+
100,
20+
180,
21+
260,
22+
380,
23+
490,
24+
600,
25+
800,
926
1001,
1027
1011,
1128
1070,
@@ -67,24 +84,28 @@ final ticksList = [
6784
194471590,
6885
];
6986

87+
final xAxisInMicroSeconds = ticksList.map(
88+
(e) => (e * 1e6) / BenchmarkHelper.frequency,
89+
);
90+
7091
final gnuplotScript = '''
7192
reset;
7293
set samples 1000;
7394
set term qt size 1000, 500 font "Sans, 14";
7495
set grid lw 2;
7596
set logscale x;
7697
unset label;
77-
set xlabel "Clock ticks";
78-
set xrange [ 1000 : 1.9e8 ] noreverse writeback;
98+
set xlabel "Single Run time [us]";
99+
set xrange [ 0 : 1e6 ] noreverse writeback;
79100
set x2range [ * : * ] noreverse writeback;
80-
set yrange [ 0 : 500 ] noreverse writeback;
101+
set yrange [ 0 : 600 ] noreverse writeback;
81102
set y2range [ * : * ] noreverse writeback;
82-
plot "sample_size.dat" using 1:2 with line lw 3 lt 2 lc "#0000FFFF" title "averaged over" at 0.3, 0.85, \
83-
"sample_size.dat" using 1:2 lw 3 lt 6 lc "#0000BBBB" title " " at 0.3, 0.85, \
84-
"sample_size.dat" using 1:3 with lines lw 3 lt 2 lc "#00FF8800" title "sample size" at 0.3, 0.77, \
85-
"sample_size.dat" using 1:3 lw 3 lt 6 lc "#00991100" title " " at 0.3, 0.77, \
86-
"sample_size.dat" using 1:4 with lines lw 3 lt 2 lc "#0000C77E" title "run time [ms]" at 0.3, 0.69, \
87-
"sample_size.dat" using 1:4 lw 3 lt 6 lc "#0000974e" title " " at 0.3, 0.69;
103+
plot "sample_size.dat" using 1:2 with line lw 3 lt 2 lc "#0000FFFF" title "averaged over" at 0.6, 0.85, \
104+
"sample_size.dat" using 1:2 lw 3 lt 6 lc "#0000BBBB" title " " at 0.6, 0.85, \
105+
"sample_size.dat" using 1:3 with lines lw 3 lt 2 lc "#00FF8800" title "sample size" at 0.6, 0.77, \
106+
"sample_size.dat" using 1:3 lw 3 lt 6 lc "#00991100" title " " at 0.6, 0.77, \
107+
"sample_size.dat" using 1:4 with lines lw 3 lt 2 lc "#0000C77E" title "total sample generation time [ms]" at 0.6, 0.69, \
108+
"sample_size.dat" using 1:4 lw 3 lt 6 lc "#0000974e" title " " at 0.6, 0.69;
88109
#
89110
''';
90111

@@ -101,17 +122,22 @@ void main(List<String> args) async {
101122
return (inner: 10, outer: 10); // This is a stub!
102123
}
103124

125+
print(xAxisInMicroSeconds);
126+
104127
// Uncomment the line below to use your custom function:
105128
// BenchmarkHelper.sampleSize = customSampleSize;
106129

107130
final b = StringBuffer();
108131
b.writeln(
109-
'# Ticks Inner-Iterations Outer-Iterations Run-Time [1 ms]');
132+
'# Microseconds Inner-Iterations Outer-Iterations Run-Time [1 ms]',
133+
);
110134

111135
for (final ticks in ticksList) {
112136
final (inner: inner, outer: outer) = BenchmarkHelper.sampleSize(ticks);
113-
b.writeln('$ticks $inner '
114-
'$outer ${ticks * inner * outer / 1000000}');
137+
b.writeln(
138+
'${ticks * 1e6 / BenchmarkHelper.frequency} $inner '
139+
'$outer ${ticks * inner * outer * 1000 / BenchmarkHelper.frequency}',
140+
);
115141
}
116142

117143
final file = await File('sample_size.dat').writeAsString(b.toString());
@@ -122,6 +148,8 @@ void main(List<String> args) async {
122148

123149
print(result.stdout);
124150
print(result.stderr);
125-
print('Returning with gnuplot exit code:'
126-
' ${result.exitCode.toString().style(Ansi.green)}');
151+
print(
152+
'Returning with gnuplot exit code:'
153+
' ${result.exitCode.toString().style(Ansi.green)}',
154+
);
127155
}

lib/benchmark_runner.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export 'src/base/async_benchmark.dart';
22
export 'src/base/benchmark.dart';
33
export 'src/base/benchmark_process_result.dart';
4-
export 'src/emitter/color_print_emitter.dart';
4+
export 'src/emitter/score_emitter.dart';
55
export 'src/base/group.dart';
66
export 'src/base/score.dart';
77
export 'src/command/benchmark_runner.dart';

0 commit comments

Comments
 (0)