Created
October 29, 2024 09:48
-
-
Save der-hugo/baadc0aa77d5d8058ce5ef643aa1e2b3 to your computer and use it in GitHub Desktop.
Tool for measuring performance and memory impact of methods
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.Runtime; | |
using System.Runtime.CompilerServices; | |
namespace derHugo | |
{ | |
public static class BenchmarkTools | |
{ | |
private static readonly object _lock = new(); | |
public readonly struct MeasurementInfo | |
{ | |
private const string FORMAT = @"Performance Measurement: {0} | |
Execution Time : {1} | {2} | |
Allocated Bytes : {3} | {4} | |
Allocations : {5} | {6} | |
"; | |
public string Method { get; } | |
public int Rounds { get; } | |
public long TotalAllocationBytes { get; } | |
public decimal AverageAllocationBytes { get; } | |
public long TotalAllocationAmount { get; } | |
public decimal AverageAllocationAmount { get; } | |
public long TotalMilliseconds { get; } | |
public decimal AverageMilliseconds { get; } | |
public MeasurementInfo(string method, int rounds, long totalAllocationBytes, long totalAllocationAmount, long totalMilliseconds) | |
{ | |
Method = method; | |
Rounds = rounds; | |
TotalAllocationBytes = totalAllocationBytes; | |
TotalAllocationAmount = totalAllocationAmount; | |
TotalMilliseconds = totalMilliseconds; | |
AverageAllocationBytes = TotalAllocationBytes / (decimal) rounds; | |
AverageAllocationAmount = TotalAllocationAmount / (decimal) rounds; | |
AverageMilliseconds = TotalMilliseconds / (decimal) rounds; | |
} | |
public override string ToString() | |
{ | |
var totalTime = $"{Rounds} rounds -> {TotalMilliseconds} ms"; | |
var averageTime = $"average {AverageMilliseconds} ms"; | |
var totalAllocationBytes = $"{Rounds} rounds -> {NicifyMemory(TotalAllocationBytes)}"; | |
var averageAllocationBytes = $"average {NicifyMemory(AverageAllocationBytes)}"; | |
var totalAllocationAmount = $"{Rounds} rounds -> {TotalAllocationAmount}"; | |
var averageAllocationAmount = $"avergae {AverageAllocationAmount}"; | |
totalTime.PadRight(totalAllocationBytes.Length); | |
totalTime.PadRight(totalAllocationAmount.Length); | |
totalAllocationBytes.PadRight(totalTime.Length); | |
totalAllocationBytes.PadRight(totalAllocationAmount.Length); | |
totalAllocationAmount.PadRight(totalTime.Length); | |
totalAllocationAmount.PadRight(totalAllocationBytes.Length); | |
averageTime.PadRight(averageAllocationBytes.Length); | |
averageTime.PadRight(averageAllocationAmount.Length); | |
averageAllocationBytes.PadRight(averageTime.Length); | |
averageAllocationBytes.PadRight(averageAllocationAmount.Length); | |
averageAllocationAmount.PadRight(averageTime.Length); | |
averageAllocationAmount.PadRight(averageAllocationBytes.Length); | |
return string.Format(FORMAT, Method, totalTime, averageTime, totalAllocationBytes, averageAllocationBytes, totalAllocationAmount, averageAllocationAmount); | |
} | |
} | |
[MethodImpl(MethodImplOptions.PreserveSig)] | |
public static MeasurementInfo MeasurePerformance(string approach, Action action, int rounds) | |
{ | |
lock (_lock) | |
{ | |
// warm-up | |
action(); | |
// memory warm-up | |
GC.Collect(); | |
GC.WaitForPendingFinalizers(); | |
GC.Collect(); | |
if (GCSettings.IsServerGC) | |
{ | |
GC.WaitForFullGCComplete(); | |
} | |
// meassure allocations | |
var allocsBefore = GC.CollectionCount(0) + GC.CollectionCount(1) + GC.CollectionCount(2); | |
var memoryBefore = GC.GetTotalMemory(true); | |
for (var x = 0; x < rounds; x++) | |
{ | |
action(); | |
} | |
var memoryAfter = GC.GetTotalMemory(false); | |
var allocsafter = GC.CollectionCount(0) + GC.CollectionCount(1) + GC.CollectionCount(2); | |
var totalAllocationBytes = memoryAfter - memoryBefore; | |
var totalAllocationAmount = allocsafter - allocsBefore; | |
// measssure time | |
var stopwatch = Stopwatch.StartNew(); | |
for (var x = 0; x < rounds; x++) | |
{ | |
action(); | |
} | |
stopwatch.Stop(); | |
var totalMilliseconds = stopwatch.ElapsedMilliseconds; | |
return new MeasurementInfo(approach, rounds, totalAllocationBytes, totalAllocationAmount, totalMilliseconds); | |
} | |
} | |
private const int KB = 1024; | |
private const int MB = KB * 1024; | |
private static string NicifyMemory(decimal bytes) | |
{ | |
return bytes switch | |
{ | |
< KB => $"{bytes} B", | |
< MB => $"{bytes / KB:F3} KB", | |
_ => $"{bytes / MB:F3} MB" | |
}; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment