Skip to content

Instantly share code, notes, and snippets.

@der-hugo
Created October 29, 2024 09:48
Show Gist options
  • Save der-hugo/baadc0aa77d5d8058ce5ef643aa1e2b3 to your computer and use it in GitHub Desktop.
Save der-hugo/baadc0aa77d5d8058ce5ef643aa1e2b3 to your computer and use it in GitHub Desktop.
Tool for measuring performance and memory impact of methods
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