@@ -19,6 +19,7 @@ public class Engine : IEngine
19
19
20
20
[ PublicAPI ] public IHost Host { get ; }
21
21
[ PublicAPI ] public Action < long > WorkloadAction { get ; }
22
+ [ PublicAPI ] public Action WorkloadActionSingleInvoke { get ; }
22
23
[ PublicAPI ] public Action Dummy1Action { get ; }
23
24
[ PublicAPI ] public Action Dummy2Action { get ; }
24
25
[ PublicAPI ] public Action Dummy3Action { get ; }
@@ -45,18 +46,21 @@ public class Engine : IEngine
45
46
private readonly EngineActualStage actualStage ;
46
47
private readonly bool includeExtraStats , includeSurvivedMemory ;
47
48
48
- private long _totalMeasuredSurvivedBytes ;
49
+ // These must be static since more than one Engine is used.
50
+ private static long survivedBytes ;
51
+ private static bool survivedBytesMeasured ;
49
52
private Func < long > GetTotalBytes { get ; }
50
53
51
54
internal Engine (
52
55
IHost host ,
53
56
IResolver resolver ,
54
- Action dummy1Action , Action dummy2Action , Action dummy3Action , Action < long > overheadAction , Action < long > workloadAction , Job targetJob ,
57
+ Action dummy1Action , Action dummy2Action , Action dummy3Action , Action < long > overheadAction , Action < long > workloadAction , Action workloadActionSingleInvoke , Job targetJob ,
55
58
Action globalSetupAction , Action globalCleanupAction , Action iterationSetupAction , Action iterationCleanupAction , long operationsPerInvoke ,
56
59
bool includeExtraStats , bool includeSurvivedMemory , string benchmarkName )
57
60
{
58
61
Host = host ;
59
62
OverheadAction = overheadAction ;
63
+ WorkloadActionSingleInvoke = workloadActionSingleInvoke ;
60
64
Dummy1Action = dummy1Action ;
61
65
Dummy2Action = dummy2Action ;
62
66
Dummy3Action = dummy3Action ;
@@ -84,14 +88,15 @@ internal Engine(
84
88
pilotStage = new EnginePilotStage ( this ) ;
85
89
actualStage = new EngineActualStage ( this ) ;
86
90
87
- GetTotalBytes = GetTotalBytesFunc ( ) ;
88
- // Necessary for CORE runtimes.
89
- if ( includeSurvivedMemory )
91
+ if ( includeSurvivedMemory && ! survivedBytesMeasured )
90
92
{
93
+ GetTotalBytes = GetTotalBytesFunc ( ) ;
94
+
95
+ // Necessary for CORE runtimes.
91
96
// Measure bytes to allow GC monitor to make its allocations.
92
97
GetTotalBytes ( ) ;
93
98
// Run the clock once to allow it to make its allocations.
94
- MeasureAction ( _ => { } , 0 ) ;
99
+ MeasureAction ( ( ) => { } ) ;
95
100
GetTotalBytes ( ) ;
96
101
}
97
102
}
@@ -100,10 +105,8 @@ private Func<long> GetTotalBytesFunc()
100
105
{
101
106
// Only enable monitoring if memory diagnoser with survived memory is applied.
102
107
// Don't try to measure in Mono, Monitoring is not available, and GC.GetTotalMemory is very inaccurate.
103
- if ( ! includeSurvivedMemory || RuntimeInformation . IsMono )
104
- {
108
+ if ( RuntimeInformation . IsMono )
105
109
return ( ) => 0 ;
106
- }
107
110
try
108
111
{
109
112
// Docs say this should be available in .NET Core 2.1, but it throws an exception.
@@ -194,20 +197,32 @@ public Measurement RunIteration(IterationData data)
194
197
bool isOverhead = data . IterationMode == IterationMode . Overhead ;
195
198
var action = isOverhead ? OverheadAction : WorkloadAction ;
196
199
200
+ double nanoseconds = 0 ;
197
201
if ( ! isOverhead )
202
+ {
198
203
IterationSetupAction ( ) ;
199
204
205
+ if ( includeSurvivedMemory && ! survivedBytesMeasured )
206
+ {
207
+ // Measure survived bytes for only the first invocation.
208
+ survivedBytesMeasured = true ;
209
+ ++ totalOperations ;
210
+ long beforeBytes = GetTotalBytes ( ) ;
211
+ nanoseconds = MeasureAction ( WorkloadActionSingleInvoke ) ;
212
+ long afterBytes = GetTotalBytes ( ) ;
213
+ survivedBytes = afterBytes - beforeBytes ;
214
+ }
215
+ }
216
+
200
217
GcCollect ( ) ;
201
218
202
219
if ( EngineEventSource . Log . IsEnabled ( ) )
203
220
EngineEventSource . Log . IterationStart ( data . IterationMode , data . IterationStage , totalOperations ) ;
204
221
205
222
// Measure
206
- long beforeBytes = GetTotalBytes ( ) ;
207
- double nanoseconds = MeasureAction ( action , invokeCount / unrollFactor ) ;
208
- long afterBytes = GetTotalBytes ( ) ;
209
- long survivedBytes = afterBytes - beforeBytes ;
210
- _totalMeasuredSurvivedBytes += survivedBytes ;
223
+ var clock = Clock . Start ( ) ;
224
+ action ( invokeCount / unrollFactor ) ;
225
+ nanoseconds += clock . GetElapsed ( ) . GetNanoseconds ( ) ;
211
226
212
227
if ( EngineEventSource . Log . IsEnabled ( ) )
213
228
EngineEventSource . Log . IterationStop ( data . IterationMode , data . IterationStage , totalOperations ) ;
@@ -218,18 +233,18 @@ public Measurement RunIteration(IterationData data)
218
233
GcCollect ( ) ;
219
234
220
235
// Results
221
- var measurement = new Measurement ( 0 , data . IterationMode , data . IterationStage , data . Index , totalOperations , nanoseconds , survivedBytes ) ;
236
+ var measurement = new Measurement ( 0 , data . IterationMode , data . IterationStage , data . Index , totalOperations , nanoseconds ) ;
222
237
WriteLine ( measurement . ToString ( ) ) ;
223
238
224
239
return measurement ;
225
240
}
226
241
227
242
// This is necessary for the CORE runtime to clean up the memory from the clock.
228
243
[ MethodImpl ( MethodImplOptions . NoInlining ) ]
229
- private double MeasureAction ( Action < long > action , long arg )
244
+ private double MeasureAction ( Action action )
230
245
{
231
246
var clock = Clock . Start ( ) ;
232
- action ( arg ) ;
247
+ action ( ) ;
233
248
return clock . GetElapsed ( ) . GetNanoseconds ( ) ;
234
249
}
235
250
@@ -252,7 +267,7 @@ private double MeasureAction(Action<long> action, long arg)
252
267
253
268
IterationCleanupAction ( ) ; // we run iteration cleanup after collecting GC stats
254
269
255
- GcStats gcStats = ( finalGcStats - initialGcStats ) . WithTotalOperationsAndSurvivedBytes ( data . InvokeCount * OperationsPerInvoke , _totalMeasuredSurvivedBytes ) ;
270
+ GcStats gcStats = ( finalGcStats - initialGcStats ) . WithTotalOperationsAndSurvivedBytes ( data . InvokeCount * OperationsPerInvoke , survivedBytes ) ;
256
271
ThreadingStats threadingStats = ( finalThreadingStats - initialThreadingStats ) . WithTotalOperations ( data . InvokeCount * OperationsPerInvoke ) ;
257
272
258
273
return ( gcStats , threadingStats ) ;
0 commit comments