@@ -45,14 +45,16 @@ public class Engine : IEngine
4545 private readonly EngineActualStage actualStage ;
4646 private readonly bool includeExtraStats , includeSurvivedMemory ;
4747
48+ private long _totalMeasuredSurvivedBytes ;
49+ private Func < long > GetTotalBytes { get ; }
50+
4851 internal Engine (
4952 IHost host ,
5053 IResolver resolver ,
5154 Action dummy1Action , Action dummy2Action , Action dummy3Action , Action < long > overheadAction , Action < long > workloadAction , Job targetJob ,
5255 Action globalSetupAction , Action globalCleanupAction , Action iterationSetupAction , Action iterationCleanupAction , long operationsPerInvoke ,
5356 bool includeExtraStats , bool includeSurvivedMemory , string benchmarkName )
5457 {
55-
5658 Host = host ;
5759 OverheadAction = overheadAction ;
5860 Dummy1Action = dummy1Action ;
@@ -82,16 +84,46 @@ internal Engine(
8284 pilotStage = new EnginePilotStage ( this ) ;
8385 actualStage = new EngineActualStage ( this ) ;
8486
87+ GetTotalBytes = GetTotalBytesFunc ( ) ;
8588 // Necessary for CORE runtimes.
8689 if ( includeSurvivedMemory )
8790 {
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 .
9194 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+ } ;
95127 }
96128 }
97129
@@ -171,9 +203,11 @@ public Measurement RunIteration(IterationData data)
171203 EngineEventSource . Log . IterationStart ( data . IterationMode , data . IterationStage , totalOperations ) ;
172204
173205 // Measure
174- GcStats . StartMeasuringSurvived ( includeSurvivedMemory ) ;
206+ long beforeBytes = GetTotalBytes ( ) ;
175207 double nanoseconds = MeasureAction ( action , invokeCount / unrollFactor ) ;
176- long survivedBytes = GcStats . StopMeasuringSurvived ( includeSurvivedMemory ) ;
208+ long afterBytes = GetTotalBytes ( ) ;
209+ long survivedBytes = afterBytes - beforeBytes ;
210+ _totalMeasuredSurvivedBytes += survivedBytes ;
177211
178212 if ( EngineEventSource . Log . IsEnabled ( ) )
179213 EngineEventSource . Log . IterationStop ( data . IterationMode , data . IterationStage , totalOperations ) ;
@@ -218,7 +252,7 @@ private double MeasureAction(Action<long> action, long arg)
218252
219253 IterationCleanupAction ( ) ; // we run iteration cleanup after collecting GC stats
220254
221- GcStats gcStats = ( finalGcStats - initialGcStats ) . WithTotalOperationsAndSurvivedBytes ( data . InvokeCount * OperationsPerInvoke ) ;
255+ GcStats gcStats = ( finalGcStats - initialGcStats ) . WithTotalOperationsAndSurvivedBytes ( data . InvokeCount * OperationsPerInvoke , _totalMeasuredSurvivedBytes ) ;
222256 ThreadingStats threadingStats = ( finalThreadingStats - initialThreadingStats ) . WithTotalOperations ( data . InvokeCount * OperationsPerInvoke ) ;
223257
224258 return ( gcStats , threadingStats ) ;
0 commit comments