Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion docs/articles/configs/diagnosers.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ In BenchmarkDotNet, 1kB = 1024B, 1MB = 1024kB, and so on. The column Gen X means

* In order to not affect main results we perform a separate run if any diagnoser is used. That's why it might take more time to execute benchmarks.
* MemoryDiagnoser:
* Mono currently [does not](https://stackoverflow.com/questions/40234948/how-to-get-the-number-of-allocated-bytes-in-mono) expose any api to get the number of allocated bytes. That's why our Mono users will get `?` in Allocated column.
* In order to get the number of allocated bytes in cross platform way we are using `GC.GetAllocatedBytesForCurrentThread` which recently got [exposed](https://github.com/dotnet/corefx/pull/12489) for netcoreapp1.1. That's why BenchmarkDotNet does not support netcoreapp1.0 from version 0.10.1.
* MemoryDiagnoser is `99.5%` accurate about allocated memory when using default settings or Job.ShortRun (or any longer job than it).
* Threading Diagnoser:
Expand Down
88 changes: 69 additions & 19 deletions src/BenchmarkDotNet/Engines/GcStats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ public struct GcStats : IEquatable<GcStats>
public static readonly long AllocationQuantum = CalculateAllocationQuantumSize();

#if !NET6_0_OR_GREATER
private static readonly Func<long> GetAllocatedBytesForCurrentThreadDelegate = CreateGetAllocatedBytesForCurrentThreadDelegate();
// do not reorder these, CheckMonitoringTotalAllocatedMemorySize relies on GetTotalAllocatedBytesDelegate being initialized first
private static readonly Func<bool, long> GetTotalAllocatedBytesDelegate = CreateGetTotalAllocatedBytesDelegate();
private static readonly Func<long> GetAllocatedBytesForCurrentThreadDelegate = CreateGetAllocatedBytesForCurrentThreadDelegate();
private static readonly bool CanUseMonitoringTotalAllocatedMemorySize = CheckMonitoringTotalAllocatedMemorySize();
#endif

public static readonly GcStats Empty = default;
Expand Down Expand Up @@ -143,9 +145,6 @@ public static GcStats FromForced(int forcedFullGarbageCollections)

private static long? GetAllocatedBytes()
{
if (RuntimeInformation.IsOldMono) // Monitoring is not available in Mono, see http://stackoverflow.com/questions/40234948/how-to-get-the-number-of-allocated-bytes-
return null;

// we have no tests for WASM and don't want to risk introducing a new bug (https://github.com/dotnet/BenchmarkDotNet/issues/2226)
if (RuntimeInformation.IsWasm)
return null;
Expand All @@ -155,37 +154,88 @@ public static GcStats FromForced(int forcedFullGarbageCollections)
// so we enforce GC.Collect here just to make sure we get accurate results
GC.Collect();

if (RuntimeInformation.IsFullFramework) // it can be a .NET app consuming our .NET Standard package
return AppDomain.CurrentDomain.MonitoringTotalAllocatedMemorySize;

#if NET6_0_OR_GREATER
return GC.GetTotalAllocatedBytes(precise: true);
#else
if (GetTotalAllocatedBytesDelegate != null) // it's .NET Core 3.0 with the new API available
return GetTotalAllocatedBytesDelegate.Invoke(true); // true for the "precise" argument

// https://apisof.net/catalog/System.GC.GetAllocatedBytesForCurrentThread() is not part of the .NET Standard, so we use reflection to call it..
return GetAllocatedBytesForCurrentThreadDelegate.Invoke();
if (CanUseMonitoringTotalAllocatedMemorySize) // Monitoring is not available in Mono, see http://stackoverflow.com/questions/40234948/how-to-get-the-number-of-allocated-bytes-
return AppDomain.CurrentDomain.MonitoringTotalAllocatedMemorySize;

if (GetAllocatedBytesForCurrentThreadDelegate != null)
return GetAllocatedBytesForCurrentThreadDelegate.Invoke();

return null;
#endif
}

#if !NET6_0_OR_GREATER
private static Func<bool, long> CreateGetTotalAllocatedBytesDelegate()
{
try
{
// this method is not a part of .NET Standard so we need to use reflection
var method = typeof(GC).GetTypeInfo().GetMethod("GetTotalAllocatedBytes", BindingFlags.Public | BindingFlags.Static);

if (method == null)
return null;

// we create delegate to avoid boxing, IMPORTANT!
var del = (Func<bool, long>)method.CreateDelegate(typeof(Func<bool, long>));

// verify the api works
return del(true) >= 0 ? del : null;
}
catch
{
return null;
}
}

private static Func<long> CreateGetAllocatedBytesForCurrentThreadDelegate()
{
// this method is not a part of .NET Standard so we need to use reflection
var method = typeof(GC).GetTypeInfo().GetMethod("GetAllocatedBytesForCurrentThread", BindingFlags.Public | BindingFlags.Static);
try
{
// this method is not a part of .NET Standard so we need to use reflection
var method = typeof(GC).GetTypeInfo().GetMethod("GetAllocatedBytesForCurrentThread", BindingFlags.Public | BindingFlags.Static);

// we create delegate to avoid boxing, IMPORTANT!
return method != null ? (Func<long>)method.CreateDelegate(typeof(Func<long>)) : null;
if (method == null)
return null;

// we create delegate to avoid boxing, IMPORTANT!
var del = (Func<long>)method.CreateDelegate(typeof(Func<long>));

// verify the api works
return del() >= 0 ? del : null;
}
catch
{
return null;
}
}

private static Func<bool, long> CreateGetTotalAllocatedBytesDelegate()
private static bool CheckMonitoringTotalAllocatedMemorySize()
{
// this method is not a part of .NET Standard so we need to use reflection
var method = typeof(GC).GetTypeInfo().GetMethod("GetTotalAllocatedBytes", BindingFlags.Public | BindingFlags.Static);
try
{
// we potentially don't want to enable monitoring if we don't need it
if (GetTotalAllocatedBytesDelegate != null)
return false;

// check if monitoring is enabled
if (!AppDomain.MonitoringIsEnabled)
AppDomain.MonitoringIsEnabled = true;

// we create delegate to avoid boxing, IMPORTANT!
return method != null ? (Func<bool, long>)method.CreateDelegate(typeof(Func<bool, long>)) : null;
// verify the api works
return AppDomain.MonitoringIsEnabled && AppDomain.CurrentDomain.MonitoringTotalAllocatedMemorySize >= 0;
}
catch
{
return false;
}
}
#endif

public string ToOutputLine()
=> $"{ResultsLinePrefix} {Gen0Collections} {Gen1Collections} {Gen2Collections} {AllocatedBytes?.ToString() ?? MetricColumn.UnknownRepresentation} {TotalOperations}";
Expand Down Expand Up @@ -261,4 +311,4 @@ private static long CalculateAllocationQuantumSize()

public override int GetHashCode() => HashCode.Combine(Gen0Collections, Gen1Collections, Gen2Collections, AllocatedBytes, TotalOperations);
}
}
}