@@ -45,14 +45,16 @@ public class Engine : IEngine
45
45
private readonly EngineActualStage actualStage ;
46
46
private readonly bool includeExtraStats , includeSurvivedMemory ;
47
47
48
+ private long _totalMeasuredSurvivedBytes ;
49
+ private Func < long > GetTotalBytes { get ; }
50
+
48
51
internal Engine (
49
52
IHost host ,
50
53
IResolver resolver ,
51
54
Action dummy1Action , Action dummy2Action , Action dummy3Action , Action < long > overheadAction , Action < long > workloadAction , Job targetJob ,
52
55
Action globalSetupAction , Action globalCleanupAction , Action iterationSetupAction , Action iterationCleanupAction , long operationsPerInvoke ,
53
56
bool includeExtraStats , bool includeSurvivedMemory , string benchmarkName )
54
57
{
55
-
56
58
Host = host ;
57
59
OverheadAction = overheadAction ;
58
60
Dummy1Action = dummy1Action ;
@@ -82,16 +84,46 @@ internal Engine(
82
84
pilotStage = new EnginePilotStage ( this ) ;
83
85
actualStage = new EngineActualStage ( this ) ;
84
86
87
+ GetTotalBytes = GetTotalBytesFunc ( ) ;
85
88
// Necessary for CORE runtimes.
86
89
if ( includeSurvivedMemory )
87
90
{
88
- // Measure survived once to allow jit to make its allocations.
89
- GcStats . StartMeasuringSurvived ( includeSurvivedMemory ) ;
90
- // Run the clock once to set static memory .
91
+ // Measure bytes to allow GC monitor to make its allocations.
92
+ GetTotalBytes ( ) ;
93
+ // Run the clock once to allow it to make its allocations .
91
94
MeasureAction ( _ => { } , 0 ) ;
92
- GcStats . StopMeasuringSurvived ( includeSurvivedMemory ) ;
93
- // Clear total measured to not pollute actual measurement.
94
- GcStats . ClearTotalMeasuredSurvived ( ) ;
95
+ GetTotalBytes ( ) ;
96
+ }
97
+ }
98
+
99
+ private Func < long > GetTotalBytesFunc ( )
100
+ {
101
+ // Only enable monitoring if memory diagnoser with survived memory is applied.
102
+ // Don't try to measure in Mono, Monitoring is not available, and GC.GetTotalMemory is very inaccurate.
103
+ if ( ! includeSurvivedMemory || RuntimeInformation . IsMono )
104
+ {
105
+ return ( ) => 0 ;
106
+ }
107
+ try
108
+ {
109
+ // Docs say this should be available in .NET Core 2.1, but it throws an exception.
110
+ // Just try this on all non-Mono runtimes, fallback to GC.GetTotalMemory.
111
+ AppDomain . MonitoringIsEnabled = true ;
112
+ return ( ) =>
113
+ {
114
+ // Enforce GC.Collect here to make sure we get accurate results.
115
+ ForceGcCollect ( ) ;
116
+ return AppDomain . CurrentDomain . MonitoringSurvivedMemorySize ;
117
+ } ;
118
+ }
119
+ catch
120
+ {
121
+ return ( ) =>
122
+ {
123
+ // Enforce GC.Collect here to make sure we get accurate results.
124
+ ForceGcCollect ( ) ;
125
+ return GC . GetTotalMemory ( true ) ;
126
+ } ;
95
127
}
96
128
}
97
129
@@ -171,9 +203,11 @@ public Measurement RunIteration(IterationData data)
171
203
EngineEventSource . Log . IterationStart ( data . IterationMode , data . IterationStage , totalOperations ) ;
172
204
173
205
// Measure
174
- GcStats . StartMeasuringSurvived ( includeSurvivedMemory ) ;
206
+ long beforeBytes = GetTotalBytes ( ) ;
175
207
double nanoseconds = MeasureAction ( action , invokeCount / unrollFactor ) ;
176
- long survivedBytes = GcStats . StopMeasuringSurvived ( includeSurvivedMemory ) ;
208
+ long afterBytes = GetTotalBytes ( ) ;
209
+ long survivedBytes = afterBytes - beforeBytes ;
210
+ _totalMeasuredSurvivedBytes += survivedBytes ;
177
211
178
212
if ( EngineEventSource . Log . IsEnabled ( ) )
179
213
EngineEventSource . Log . IterationStop ( data . IterationMode , data . IterationStage , totalOperations ) ;
@@ -218,7 +252,7 @@ private double MeasureAction(Action<long> action, long arg)
218
252
219
253
IterationCleanupAction ( ) ; // we run iteration cleanup after collecting GC stats
220
254
221
- GcStats gcStats = ( finalGcStats - initialGcStats ) . WithTotalOperationsAndSurvivedBytes ( data . InvokeCount * OperationsPerInvoke ) ;
255
+ GcStats gcStats = ( finalGcStats - initialGcStats ) . WithTotalOperationsAndSurvivedBytes ( data . InvokeCount * OperationsPerInvoke , _totalMeasuredSurvivedBytes ) ;
222
256
ThreadingStats threadingStats = ( finalThreadingStats - initialThreadingStats ) . WithTotalOperations ( data . InvokeCount * OperationsPerInvoke ) ;
223
257
224
258
return ( gcStats , threadingStats ) ;
0 commit comments