44using System . Globalization ;
55using System . Linq ;
66using System . Runtime . CompilerServices ;
7+ using System . Threading . Tasks ;
78using BenchmarkDotNet . Characteristics ;
89using BenchmarkDotNet . Jobs ;
910using BenchmarkDotNet . Portability ;
@@ -19,17 +20,17 @@ public class Engine : IEngine
1920 public const int MinInvokeCount = 4 ;
2021
2122 [ PublicAPI ] public IHost Host { get ; }
22- [ PublicAPI ] public Action < long > WorkloadAction { get ; }
23+ [ PublicAPI ] public Func < long , IClock , ValueTask < ClockSpan > > WorkloadAction { get ; }
2324 [ PublicAPI ] public Action Dummy1Action { get ; }
2425 [ PublicAPI ] public Action Dummy2Action { get ; }
2526 [ PublicAPI ] public Action Dummy3Action { get ; }
26- [ PublicAPI ] public Action < long > OverheadAction { get ; }
27+ [ PublicAPI ] public Func < long , IClock , ValueTask < ClockSpan > > OverheadAction { get ; }
2728 [ PublicAPI ] public Job TargetJob { get ; }
2829 [ 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 ; }
3334 [ PublicAPI ] public IResolver Resolver { get ; }
3435 [ PublicAPI ] public CultureInfo CultureInfo { get ; }
3536 [ PublicAPI ] public string BenchmarkName { get ; }
@@ -46,13 +47,14 @@ public class Engine : IEngine
4647 private readonly EngineActualStage actualStage ;
4748 private readonly bool includeExtraStats ;
4849 private readonly Random random ;
50+ private readonly Helpers . AwaitHelper awaitHelper ;
4951
5052 internal Engine (
5153 IHost host ,
5254 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 )
5658 {
5759
5860 Host = host ;
@@ -84,13 +86,14 @@ internal Engine(
8486 actualStage = new EngineActualStage ( this ) ;
8587
8688 random = new Random ( 12345 ) ; // we are using constant seed to try to get repeatable results
89+ awaitHelper = new Helpers . AwaitHelper ( ) ;
8790 }
8891
8992 public void Dispose ( )
9093 {
9194 try
9295 {
93- GlobalCleanupAction ? . Invoke ( ) ;
96+ awaitHelper . GetResult ( GlobalCleanupAction . Invoke ( ) ) ;
9497 }
9598 catch ( Exception e )
9699 {
@@ -155,7 +158,7 @@ public Measurement RunIteration(IterationData data)
155158 var action = isOverhead ? OverheadAction : WorkloadAction ;
156159
157160 if ( ! isOverhead )
158- IterationSetupAction ( ) ;
161+ awaitHelper . GetResult ( IterationSetupAction ( ) ) ;
159162
160163 GcCollect ( ) ;
161164
@@ -165,15 +168,14 @@ public Measurement RunIteration(IterationData data)
165168 Span < byte > stackMemory = randomizeMemory ? stackalloc byte [ random . Next ( 32 ) ] : Span < byte > . Empty ;
166169
167170 // 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 ) ;
171173
172174 if ( EngineEventSource . Log . IsEnabled ( ) )
173175 EngineEventSource . Log . IterationStop ( data . IterationMode , data . IterationStage , totalOperations ) ;
174176
175177 if ( ! isOverhead )
176- IterationCleanupAction ( ) ;
178+ awaitHelper . GetResult ( IterationCleanupAction ( ) ) ;
177179
178180 if ( randomizeMemory )
179181 RandomizeManagedHeapMemory ( ) ;
@@ -196,17 +198,18 @@ public Measurement RunIteration(IterationData data)
196198 // it does not matter, because we have already obtained the results!
197199 EnableMonitoring ( ) ;
198200
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
200202
201203 var initialThreadingStats = ThreadingStats . ReadInitial ( ) ; // this method might allocate
202204 var initialGcStats = GcStats . ReadInitial ( ) ;
203205
204- WorkloadAction ( data . InvokeCount / data . UnrollFactor ) ;
206+ var op = WorkloadAction ( data . InvokeCount / data . UnrollFactor , Clock ) ;
207+ awaitHelper . GetResult ( op ) ;
205208
206209 var finalGcStats = GcStats . ReadFinal ( ) ;
207210 var finalThreadingStats = ThreadingStats . ReadFinal ( ) ;
208211
209- IterationCleanupAction ( ) ; // we run iteration cleanup after collecting GC stats
212+ awaitHelper . GetResult ( IterationCleanupAction ( ) ) ; // we run iteration cleanup after collecting GC stats
210213
211214 GcStats gcStats = ( finalGcStats - initialGcStats ) . WithTotalOperations ( data . InvokeCount * OperationsPerInvoke ) ;
212215 ThreadingStats threadingStats = ( finalThreadingStats - initialThreadingStats ) . WithTotalOperations ( data . InvokeCount * OperationsPerInvoke ) ;
@@ -220,14 +223,14 @@ private void Consume(in Span<byte> _) { }
220223 private void RandomizeManagedHeapMemory ( )
221224 {
222225 // invoke global cleanup before global setup
223- GlobalCleanupAction ? . Invoke ( ) ;
226+ awaitHelper . GetResult ( GlobalCleanupAction . Invoke ( ) ) ;
224227
225228 var gen0object = new byte [ random . Next ( 32 ) ] ;
226229 var lohObject = new byte [ 85 * 1024 + random . Next ( 32 ) ] ;
227230
228231 // we expect the key allocations to happen in global setup (not ctor)
229232 // so we call it while keeping the random-size objects alive
230- GlobalSetupAction ? . Invoke ( ) ;
233+ awaitHelper . GetResult ( GlobalSetupAction . Invoke ( ) ) ;
231234
232235 GC . KeepAlive ( gen0object ) ;
233236 GC . KeepAlive ( lohObject ) ;
0 commit comments