55library ;
66
77import 'dart:isolate' ;
8+ import 'dart:math' ;
89
910import 'package:isolate_manager/isolate_manager.dart' ;
1011import 'package:test/test.dart' ;
1112
1213import 'functions.dart' ;
1314import 'utils.dart' ;
1415
16+ const samples = 30 ;
17+
1518void main () {
1619 test ('benchmark' , () async {
1720 printDebug (
@@ -20,44 +23,63 @@ void main() {
2023 );
2124 printDebug (() => '|:-:|-:|-:|-:|-:|-:|-:|' );
2225
23- // Warmup: run each part once to trigger JIT
24- await warmup (30 );
25- await warmup (33 );
26- await warmup (36 );
26+ // Stronger warmup
27+ await warmup (23 );
28+ await warmup (26 );
29+ await warmup (29 );
2730
28- // Run benchmarks
29- await benchmarkFor (30 );
30- await benchmarkFor (33 );
31- await benchmarkFor (36 );
31+ await benchmarkFor (23 );
32+ await benchmarkFor (26 );
33+ await benchmarkFor (29 );
3234 }, timeout: const Timeout (Duration (seconds: 180 )));
3335}
3436
35- /// Run one warmup round to reduce JIT overhead.
3637Future <void > warmup (int fibonacciNumber) async {
37- await execute (fibonacciNumber, iterations: 5 , warmupOnly: true );
38+ final iterations = switch (fibonacciNumber) {
39+ 23 => 20 ,
40+ 26 => 10 ,
41+ 29 => 5 ,
42+ _ => 10 ,
43+ };
44+
45+ await execute (fibonacciNumber, iterations: iterations, warmupOnly: true );
3846}
3947
40- /// Benchmark each method for the given fibonacci number.
4148Future <void > benchmarkFor (int fibonacciNumber) async {
42- final result = await execute (fibonacciNumber, iterations: 50 );
49+ final iterations = switch (fibonacciNumber) {
50+ 23 => 50 ,
51+ 26 => 30 ,
52+ 29 => 10 ,
53+ _ => 50 ,
54+ };
55+
56+ final result = await execute (fibonacciNumber, iterations: iterations);
4357 printDebug (() => result);
4458}
4559
46- /// Runs different methods with a separate measurement function.
47- /// If [warmupOnly] is set, no measurement will be taken.
4860Future <String > execute (
4961 int fibonacciNumber, {
5062 required int iterations,
5163 bool warmupOnly = false ,
5264}) async {
5365 final results = < String , Duration > {};
5466
55- // Use a function to measure elapsed time for an async action.
56- Future <Duration > measure (Future <void > Function () action) async {
57- final watch = Stopwatch ()..start ();
58- await action ();
59- watch.stop ();
60- return watch.elapsed;
67+ Future <Duration > measureStable (Future <void > Function () action) async {
68+ final samplesList = < int > [];
69+
70+ for (var i = 0 ; i < samples; i++ ) {
71+ final watch = Stopwatch ()..start ();
72+ await action ();
73+ watch.stop ();
74+ samplesList.add (watch.elapsedMicroseconds);
75+ }
76+
77+ samplesList.sort ();
78+
79+ // Median (more stable than average)
80+ final median = samplesList[samplesList.length ~ / 2 ];
81+
82+ return Duration (microseconds: median);
6183 }
6284
6385 Future <void > runMainApp () async {
@@ -69,9 +91,11 @@ Future<String> execute(
6991 Future <void > runOneIsolate () async {
7092 final singleIsolate = IsolateManager <int , int >.create (fibonacciRecursive);
7193 await singleIsolate.start ();
94+
7295 for (var i = 0 ; i < iterations; i++ ) {
7396 await singleIsolate.compute (fibonacciNumber);
7497 }
98+
7599 await singleIsolate.stop ();
76100 }
77101
@@ -80,11 +104,14 @@ Future<String> execute(
80104 fibonacciRecursive,
81105 concurrent: 3 ,
82106 );
107+
83108 await threeIsolates.start ();
109+
84110 await Future .wait ([
85111 for (int i = 0 ; i < iterations; i++ )
86112 threeIsolates.compute (fibonacciNumber),
87113 ]);
114+
88115 await threeIsolates.stop ();
89116 }
90117
@@ -106,29 +133,41 @@ Future<String> execute(
106133 }
107134 }
108135
136+ final tests = < String , Future <void > Function ()> {
137+ 'Main App' : runMainApp,
138+ 'One Isolate' : runOneIsolate,
139+ 'Three Isolates' : runThreeIsolates,
140+ 'IsolateManager.runFunction' : runIsolateManagerFunction,
141+ 'IsolateManager.run' : runIsolateManagerRun,
142+ 'Isolate.run' : runIsolateRun,
143+ };
144+
109145 if (warmupOnly) {
110- await runMainApp ();
111- await runOneIsolate ();
112- await runThreeIsolates ();
113- await runIsolateManagerFunction ();
114- await runIsolateManagerRun ();
115- await runIsolateRun ();
146+ for (final fn in tests.values) {
147+ await fn ();
148+ }
116149 return '' ;
117150 }
118151
119- results['Main App' ] = await measure (runMainApp);
120- results['One Isolate' ] = await measure (runOneIsolate);
121- results['Three Isolates' ] = await measure (runThreeIsolates);
122- results['IsolateManager.runFunction' ] = await measure (
123- runIsolateManagerFunction,
124- );
125- results['IsolateManager.run' ] = await measure (runIsolateManagerRun);
126- results['Isolate.run' ] = await measure (runIsolateRun);
152+ // Shuffle test order to remove bias
153+ final entries = tests.entries.toList ()..shuffle (Random ());
154+
155+ for (final entry in entries) {
156+ results[entry.key] = await measureStable (entry.value);
157+ }
127158
128- // Convert durations to average microseconds per iteration
129- String format (Duration d) => d.inMicroseconds.asThousands ();
159+ String format (Duration d) {
160+ final perIteration = d.inMicroseconds ~ / iterations;
161+ return perIteration.asThousands ();
162+ }
130163
131- return '|$fibonacciNumber |${format (results ['Main App' ]!)}|${format (results ['One Isolate' ]!)}|${format (results ['Three Isolates' ]!)}|${format (results ['IsolateManager.runFunction' ]!)}|${format (results ['IsolateManager.run' ]!)}|${format (results ['Isolate.run' ]!)}|' ;
164+ return '|$fibonacciNumber |'
165+ '${format (results ['Main App' ]!)}|'
166+ '${format (results ['One Isolate' ]!)}|'
167+ '${format (results ['Three Isolates' ]!)}|'
168+ '${format (results ['IsolateManager.runFunction' ]!)}|'
169+ '${format (results ['IsolateManager.run' ]!)}|'
170+ '${format (results ['Isolate.run' ]!)}|' ;
132171}
133172
134173void printDebug (Object ? Function () log) {
0 commit comments