4
4
using System . Globalization ;
5
5
using System . Linq ;
6
6
using System . Runtime . CompilerServices ;
7
+ using System . Threading . Tasks ;
7
8
using BenchmarkDotNet . Characteristics ;
8
9
using BenchmarkDotNet . Jobs ;
9
10
using BenchmarkDotNet . Portability ;
@@ -19,17 +20,17 @@ public class Engine : IEngine
19
20
public const int MinInvokeCount = 4 ;
20
21
21
22
[ PublicAPI ] public IHost Host { get ; }
22
- [ PublicAPI ] public Action < long > WorkloadAction { get ; }
23
+ [ PublicAPI ] public Func < long , IClock , ValueTask < ClockSpan > > WorkloadAction { get ; }
23
24
[ PublicAPI ] public Action Dummy1Action { get ; }
24
25
[ PublicAPI ] public Action Dummy2Action { get ; }
25
26
[ PublicAPI ] public Action Dummy3Action { get ; }
26
- [ PublicAPI ] public Action < long > OverheadAction { get ; }
27
+ [ PublicAPI ] public Func < long , IClock , ValueTask < ClockSpan > > OverheadAction { get ; }
27
28
[ PublicAPI ] public Job TargetJob { get ; }
28
29
[ PublicAPI ] public long OperationsPerInvoke { get ; }
29
- [ PublicAPI ] public Action GlobalSetupAction { get ; }
30
- [ PublicAPI ] public Action GlobalCleanupAction { get ; }
31
- [ PublicAPI ] public Action IterationSetupAction { get ; }
32
- [ PublicAPI ] public Action IterationCleanupAction { get ; }
30
+ [ PublicAPI ] public Func < ValueTask > GlobalSetupAction { get ; }
31
+ [ PublicAPI ] public Func < ValueTask > GlobalCleanupAction { get ; }
32
+ [ PublicAPI ] public Func < ValueTask > IterationSetupAction { get ; }
33
+ [ PublicAPI ] public Func < ValueTask > IterationCleanupAction { get ; }
33
34
[ PublicAPI ] public IResolver Resolver { get ; }
34
35
[ PublicAPI ] public CultureInfo CultureInfo { get ; }
35
36
[ PublicAPI ] public string BenchmarkName { get ; }
@@ -46,13 +47,14 @@ public class Engine : IEngine
46
47
private readonly EngineActualStage actualStage ;
47
48
private readonly bool includeExtraStats ;
48
49
private readonly Random random ;
50
+ private readonly Helpers . AwaitHelper awaitHelper ;
49
51
50
52
internal Engine (
51
53
IHost host ,
52
54
IResolver resolver ,
53
- Action dummy1Action , Action dummy2Action , Action dummy3Action , Action < long > overheadAction , Action < long > workloadAction , Job targetJob ,
54
- Action globalSetupAction , Action globalCleanupAction , Action iterationSetupAction , Action iterationCleanupAction , long operationsPerInvoke ,
55
- bool includeExtraStats , string benchmarkName )
55
+ Action dummy1Action , Action dummy2Action , Action dummy3Action , Func < long , IClock , ValueTask < ClockSpan > > overheadAction , Func < long , IClock , ValueTask < ClockSpan > > workloadAction ,
56
+ Job targetJob , Func < ValueTask > globalSetupAction , Func < ValueTask > globalCleanupAction , Func < ValueTask > iterationSetupAction , Func < ValueTask > iterationCleanupAction ,
57
+ long operationsPerInvoke , bool includeExtraStats , string benchmarkName )
56
58
{
57
59
58
60
Host = host ;
@@ -84,13 +86,14 @@ internal Engine(
84
86
actualStage = new EngineActualStage ( this ) ;
85
87
86
88
random = new Random ( 12345 ) ; // we are using constant seed to try to get repeatable results
89
+ awaitHelper = new Helpers . AwaitHelper ( ) ;
87
90
}
88
91
89
92
public void Dispose ( )
90
93
{
91
94
try
92
95
{
93
- GlobalCleanupAction ? . Invoke ( ) ;
96
+ awaitHelper . GetResult ( GlobalCleanupAction . Invoke ( ) ) ;
94
97
}
95
98
catch ( Exception e )
96
99
{
@@ -155,7 +158,7 @@ public Measurement RunIteration(IterationData data)
155
158
var action = isOverhead ? OverheadAction : WorkloadAction ;
156
159
157
160
if ( ! isOverhead )
158
- IterationSetupAction ( ) ;
161
+ awaitHelper . GetResult ( IterationSetupAction ( ) ) ;
159
162
160
163
GcCollect ( ) ;
161
164
@@ -165,15 +168,14 @@ public Measurement RunIteration(IterationData data)
165
168
Span < byte > stackMemory = randomizeMemory ? stackalloc byte [ random . Next ( 32 ) ] : Span < byte > . Empty ;
166
169
167
170
// Measure
168
- var clock = Clock . Start ( ) ;
169
- action ( invokeCount / unrollFactor ) ;
170
- var clockSpan = clock . GetElapsed ( ) ;
171
+ var op = action ( invokeCount / unrollFactor , Clock ) ;
172
+ var clockSpan = awaitHelper . GetResult ( op ) ;
171
173
172
174
if ( EngineEventSource . Log . IsEnabled ( ) )
173
175
EngineEventSource . Log . IterationStop ( data . IterationMode , data . IterationStage , totalOperations ) ;
174
176
175
177
if ( ! isOverhead )
176
- IterationCleanupAction ( ) ;
178
+ awaitHelper . GetResult ( IterationCleanupAction ( ) ) ;
177
179
178
180
if ( randomizeMemory )
179
181
RandomizeManagedHeapMemory ( ) ;
@@ -196,17 +198,18 @@ public Measurement RunIteration(IterationData data)
196
198
// it does not matter, because we have already obtained the results!
197
199
EnableMonitoring ( ) ;
198
200
199
- IterationSetupAction ( ) ; // we run iteration setup first, so even if it allocates, it is not included in the results
201
+ awaitHelper . GetResult ( IterationSetupAction ( ) ) ; // we run iteration setup first, so even if it allocates, it is not included in the results
200
202
201
203
var initialThreadingStats = ThreadingStats . ReadInitial ( ) ; // this method might allocate
202
204
var initialGcStats = GcStats . ReadInitial ( ) ;
203
205
204
- WorkloadAction ( data . InvokeCount / data . UnrollFactor ) ;
206
+ var op = WorkloadAction ( data . InvokeCount / data . UnrollFactor , Clock ) ;
207
+ awaitHelper . GetResult ( op ) ;
205
208
206
209
var finalGcStats = GcStats . ReadFinal ( ) ;
207
210
var finalThreadingStats = ThreadingStats . ReadFinal ( ) ;
208
211
209
- IterationCleanupAction ( ) ; // we run iteration cleanup after collecting GC stats
212
+ awaitHelper . GetResult ( IterationCleanupAction ( ) ) ; // we run iteration cleanup after collecting GC stats
210
213
211
214
GcStats gcStats = ( finalGcStats - initialGcStats ) . WithTotalOperations ( data . InvokeCount * OperationsPerInvoke ) ;
212
215
ThreadingStats threadingStats = ( finalThreadingStats - initialThreadingStats ) . WithTotalOperations ( data . InvokeCount * OperationsPerInvoke ) ;
@@ -220,14 +223,14 @@ private void Consume(in Span<byte> _) { }
220
223
private void RandomizeManagedHeapMemory ( )
221
224
{
222
225
// invoke global cleanup before global setup
223
- GlobalCleanupAction ? . Invoke ( ) ;
226
+ awaitHelper . GetResult ( GlobalCleanupAction . Invoke ( ) ) ;
224
227
225
228
var gen0object = new byte [ random . Next ( 32 ) ] ;
226
229
var lohObject = new byte [ 85 * 1024 + random . Next ( 32 ) ] ;
227
230
228
231
// we expect the key allocations to happen in global setup (not ctor)
229
232
// so we call it while keeping the random-size objects alive
230
- GlobalSetupAction ? . Invoke ( ) ;
233
+ awaitHelper . GetResult ( GlobalSetupAction . Invoke ( ) ) ;
231
234
232
235
GC . KeepAlive ( gen0object ) ;
233
236
GC . KeepAlive ( lohObject ) ;
0 commit comments