Skip to content

Instantly share code, notes, and snippets.

@ZodmanPerth
Last active December 30, 2018 08:24
Show Gist options
  • Save ZodmanPerth/12711f70d8db12cf4af80ee7df7f641e to your computer and use it in GitHub Desktop.
Save ZodmanPerth/12711f70d8db12cf4af80ee7df7f641e to your computer and use it in GitHub Desktop.
PerformanceDotNet in LINQPad with report amalgamation. Blog post at http://www.redperegrine.net/2017/12/23/measuring-csharp-perf-with-benchmarkdotnet/
//This LINQPad file is discussed in my blog post:
// http://www.redperegrine.net/2017/12/10/measuring-csharp-perf-with-benchmarkdotnet/
[MemoryDiagnoser]
[RyuJitX64Job, LegacyJitX86Job]
public class IntegerWorker
{
[Benchmark]
public void NormalEnumerator()
{
IntegerDriver driver = new IntegerDriver();
int count = 0;
int sum = 0;
foreach (int i in driver)
{
sum += i;
count++;
}
if (RunnerHelper.SupressBenchmarking)
RunnerHelper.DumpWorkerReport(sum, count);
}
[Benchmark]
public void HandCrankedEnumerator()
{
IntegerDriver driver = new IntegerDriver();
int count = 0;
int sum = 0;
driver.Reset();
while (!driver.EOF)
{
sum += driver.Current;
count++;
driver.MoveNext();
}
if (RunnerHelper.SupressBenchmarking)
RunnerHelper.DumpWorkerReport(sum, count);
}
[Benchmark]
public void HandCrankedWithMoveNextState()
{
IntegerDriver driver = new IntegerDriver();
int count = 0;
int sum = 0;
if (driver.ResetWithState())
do
{
sum += driver.Current;
count++;
}
while (driver.MoveNextWithState());
if (RunnerHelper.SupressBenchmarking)
RunnerHelper.DumpWorkerReport(sum, count);
}
[Benchmark]
public void ResultSetFullyCalculated()
{
IntegerDriver driver = new IntegerDriver();
int count = 0;
int sum = 0;
driver.Calculate();
count = driver.ResultCount;
for (int i = 0; i < count; i++)
sum += driver.Current;
if (RunnerHelper.SupressBenchmarking)
RunnerHelper.DumpWorkerReport(sum, count);
}
}
[MemoryDiagnoser]
[RyuJitX64Job, LegacyJitX86Job]
public class ItemWorker
{
[Benchmark]
public void NormalEnumerator()
{
ItemDriver driver = new ItemDriver();
int count = 0;
int sum = 0;
foreach (Item i in driver)
{
sum += i.Value;
count++;
}
if (RunnerHelper.SupressBenchmarking)
RunnerHelper.DumpWorkerReport(sum, count);
}
[Benchmark]
public void HandCrankedEnumerator()
{
ItemDriver driver = new ItemDriver();
int count = 0;
int sum = 0;
driver.Reset();
while (!driver.EOF)
{
sum += driver.Current.Value;
count++;
driver.MoveNext();
}
if (RunnerHelper.SupressBenchmarking)
RunnerHelper.DumpWorkerReport(sum, count);
}
[Benchmark]
public void HandCrankedWithMoveNextState()
{
ItemDriver driver = new ItemDriver();
int count = 0;
int sum = 0;
if (driver.ResetWithState())
do
{
sum += driver.Current.Value;
count++;
}
while (driver.MoveNextWithState());
if (RunnerHelper.SupressBenchmarking)
RunnerHelper.DumpWorkerReport(sum, count);
}
[Benchmark]
public void ResultSetFullyCalculated()
{
ItemDriver driver = new ItemDriver();
int count = 0;
int sum = 0;
driver.Calculate();
count = driver.ResultCount;
for (int i = 0; i < count; i++)
sum += driver.Current.Value;
if (RunnerHelper.SupressBenchmarking)
RunnerHelper.DumpWorkerReport(sum, count);
}
}
[MemoryDiagnoser]
[RyuJitX64Job, LegacyJitX86Job]
public class GenericWorker
{
[Benchmark]
public void NormalEnumerator()
{
GenericDriver<Item> driver = new GenericDriver<Item>();
int count = 0;
int sum = 0;
foreach (Item i in driver)
{
sum += i.Value;
count++;
}
if (RunnerHelper.SupressBenchmarking)
RunnerHelper.DumpWorkerReport(sum, count);
}
[Benchmark]
public void HandCrankedEnumerator()
{
GenericDriver<Item> driver = new GenericDriver<Item>();
int count = 0;
int sum = 0;
driver.Reset();
while (!driver.EOF)
{
sum += driver.Current.Value;
count++;
driver.MoveNext();
}
if (RunnerHelper.SupressBenchmarking)
RunnerHelper.DumpWorkerReport(sum, count);
}
[Benchmark]
public void HandCrankedWithMoveNextState()
{
GenericDriver<Item> driver = new GenericDriver<Item>();
int count = 0;
int sum = 0;
if (driver.ResetWithState())
do
{
sum += driver.Current.Value;
count++;
}
while (driver.MoveNextWithState());
if (RunnerHelper.SupressBenchmarking)
RunnerHelper.DumpWorkerReport(sum, count);
}
[Benchmark]
public void ResultSetFullyCalculated()
{
GenericDriver<Item> driver = new GenericDriver<Item>();
int count = 0;
int sum = 0;
driver.Calculate();
count = driver.ResultCount;
for (int i = 0; i < count; i++)
sum += driver.Current.Value;
if (RunnerHelper.SupressBenchmarking)
RunnerHelper.DumpWorkerReport(sum, count);
}
}
static class RunnerHelper
{
const string _lpPath = @"C:\Program Files (x86)\LINQPad5\BenchmarkDotNet.Artifacts\results\";
public static string ReportType { get; set; }
public static string ReportFilePath
{
get
{
if (string.IsNullOrEmpty(ReportType)) throw new ArgumentNullException($"({nameof(ReportType)}");
return $@"{_lpPath}UserQuery+{ReportType}-report.html";
}
}
///<summary>
///Construct a file path in the same folder as the report with the passed file name
///</summary>
static string GenerateNewReportFilePath()
{
return $@"{_lpPath}{ReportType}-{Size}.html";
}
public static int Size { get; set; }
public static bool SupressBenchmarking { get; set; }
static StringBuilder _reportPaths;
///<summary>
///Copy the report from BenchmarkDotNet into the same folder with a new filename.
///</summary>
public static void BackupReport()
{
if (File.Exists(ReportFilePath))
{
var filename = GenerateNewReportFilePath();
if (_reportPaths == null)
_reportPaths = new StringBuilder();
_reportPaths.AppendLine(filename);
if (File.Exists(filename))
File.Delete(filename);
if (SupressBenchmarking)
$"Copying file:\n{ReportFilePath}\nTo file:\n{filename}".Dump();
File.Copy(ReportFilePath, filename);
}
else
{
if (SupressBenchmarking)
"Unable to backup report; not found.".Dump();
}
}
///<summary>
///Dump the previously backed up reports with a heading.
///</summary>
public static void DumpReports(string heading = null)
{
if (_reportPaths == null || _reportPaths.Length == 0)
"Nothing to report".Dump();
if (!string.IsNullOrEmpty(heading))
"Report:".Dump(heading);
var parser = new HtmlParser();
bool isHeaderAdded = false;
StringBuilder html = new StringBuilder("<table>");
foreach (var path in _reportPaths.ToString().Split('\n'))
{
if (path.Length == 0)
continue;
var filepath = path.Trim();
if (File.Exists(filepath))
{
var fileparts = System.IO.Path.GetFileNameWithoutExtension(filepath)
.Split('-');
var type = fileparts[0];
var size = fileparts[1];
var doc = parser.Parse(File.ReadAllText(filepath));
if (!isHeaderAdded)
{
html.AppendLine($"<thead><tr><th>Type</th><th>Size</th>{doc.QuerySelector("thead > tr").InnerHtml}</tr></thead>");
isHeaderAdded = true;
}
var rows = doc.QuerySelectorAll("tbody > tr");
foreach (var r in rows)
html.Append($"<tbody><tr><td>{type}</td><td>{size}</td>{r.InnerHtml}</tr></tbody>");
}
else
$"Unable to find report '{filepath}'".Dump();
}
if (isHeaderAdded)
{
html.AppendLine("</table>");
Util.RawHtml(html.ToString()).Dump();
File.WriteAllText($@"{_lpPath}AmalgamatedReport.html", html.ToString());
}
}
///<summary>
///Dump the report directly from BenchmarkDotNet
///</summary>
public static void DumpReport()
{
if (File.Exists(ReportFilePath))
Util.RawHtml(File.ReadAllText(ReportFilePath)).Dump();
else
$"Unable to find report '{ReportFilePath}'".Dump();
}
public static void DumpWorkerReport(int sum, int count)
{
$"Sum: {sum}, Count: {count}".Dump();
}
}
void Main()
{
//Set execution state
var sizes = new int[] { 10, 1000, 10000 };
RunnerHelper.SupressBenchmarking = false;
if (RunnerHelper.SupressBenchmarking)
{
"Running manual validation of test results".Dump("Execution Type");
for (int i = 0; i < sizes.Length; i++)
{
RunnerHelper.Size = sizes[i];
var integerWorker = new IntegerWorker();
integerWorker.NormalEnumerator();
integerWorker.HandCrankedEnumerator();
integerWorker.HandCrankedWithMoveNextState();
integerWorker.ResultSetFullyCalculated();
var itemWorker = new ItemWorker();
itemWorker.NormalEnumerator();
itemWorker.HandCrankedEnumerator();
itemWorker.HandCrankedWithMoveNextState();
itemWorker.ResultSetFullyCalculated();
var genericWorkerInt = new GenericWorker();
genericWorkerInt.NormalEnumerator();
genericWorkerInt.HandCrankedEnumerator();
genericWorkerInt.HandCrankedWithMoveNextState();
genericWorkerInt.ResultSetFullyCalculated();
}
}
else
{
"Running tests with BenchmarkDotNet".Dump("Execution Type");
for (int i = 0; i < sizes.Length; i++)
{
RunnerHelper.Size = sizes[i];
RunnerHelper.ReportType = "IntegerWorker";
BenchmarkRunner.Run<IntegerWorker>();
RunnerHelper.BackupReport();
RunnerHelper.ReportType = "ItemWorker";
BenchmarkRunner.Run<ItemWorker>();
RunnerHelper.BackupReport();
RunnerHelper.ReportType = "GenericWorker";
BenchmarkRunner.Run<GenericWorker>();
RunnerHelper.BackupReport();
}
//Report
RunnerHelper.DumpReports();
}
}
//class CustomConfig : ManualConfig
//{
// public CustomConfig()
// {
// Add(MemoryDiagnoser.Default);
//
// Add(JitOptimizationsValidator.DontFailOnError); // ALLOW NON-OPTIMIZED DLLS
//
// Add(DefaultConfig.Instance.GetLoggers().ToArray()); // manual config has no loggers by default
// Add(DefaultConfig.Instance.GetExporters().ToArray()); // manual config has no exporters by default
// Add(DefaultConfig.Instance.GetColumnProviders().ToArray()); // manual config has no columns by default
// }
//}
interface IHasValue
{
int Value { get; set; }
}
class Item : IHasValue
{
public int Value { get; set; }
}
class GenericDriver<T> : IEnumerable<T> where T : IHasValue, new()
{
T[] _array;
public T Current { get { return _array[_index]; } }
public GenericDriver()
{
_array = new T[RunnerHelper.Size];
for (int i = 0; i < RunnerHelper.Size; i++)
_array[i] = new T() { Value = 1 };
Results = new T[RunnerHelper.Size];
}
public IEnumerator<T> GetEnumerator()
{
for (int i = 0; i < RunnerHelper.Size; i++)
yield return _array[i];
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
int _index = 0;
public void Reset() { _index = 0; }
public T MoveNext() { return _array[_index++]; }
public bool EOF { get { return _index >= RunnerHelper.Size; } }
public bool ResetWithState() { _index = 0; return _index < RunnerHelper.Size; }
public bool MoveNextWithState()
{
_index++;
if (_index == RunnerHelper.Size)
return false;
return true;
}
public T[] Results { get; set; }
public int ResultCount { get; set; }
public void Calculate()
{
ResultCount = 0;
for (int i = 0; i < RunnerHelper.Size; i++)
{
Results[i] = _array[i];
ResultCount++;
}
}
}
class IntegerDriver : IEnumerable<int>
{
int[] _array;
public int Current { get { return _array[_index]; } }
public IntegerDriver()
{
_array = new int[RunnerHelper.Size];
for (int i = 0; i < RunnerHelper.Size; i++)
_array[i] = 1;
Results = new int[RunnerHelper.Size];
}
public IEnumerator<int> GetEnumerator()
{
for (int i = 0; i < RunnerHelper.Size; i++)
yield return _array[i];
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
int _index = 0;
public void Reset() { _index = 0; }
public int MoveNext() { return _array[_index++]; }
public bool EOF { get { return _index >= RunnerHelper.Size; } }
public bool ResetWithState() { _index = 0; return _index < RunnerHelper.Size; }
public bool MoveNextWithState()
{
_index++;
if (_index == RunnerHelper.Size)
return false;
return true;
}
public int[] Results { get; set; }
public int ResultCount { get; set; }
public void Calculate()
{
ResultCount = 0;
for (int i = 0; i < RunnerHelper.Size; i++)
{
Results[i] = _array[i];
ResultCount++;
}
}
}
class SpecificItemDriver
{
int _index = 0;
Item[] _array;
public Item Current { get { return _array[_index]; } }
public SpecificItemDriver()
{
_array = new Item[RunnerHelper.Size];
for (int i = 0; i < RunnerHelper.Size; i++)
_array[i] = new Item() { Value = 1 };
}
public void Reset() { _index = 0; }
public Item MoveNext() { return _array[_index++]; }
public bool EOF { get { return _index >= RunnerHelper.Size; } }
}
class ItemDriver : IEnumerable<Item>
{
Item[] _array;
public Item Current { get { return _array[_index]; } }
public ItemDriver()
{
_array = new Item[RunnerHelper.Size];
for (int i = 0; i < RunnerHelper.Size; i++)
_array[i] = new Item() { Value = 1 };
Results = new Item[RunnerHelper.Size];
}
public IEnumerator<Item> GetEnumerator()
{
for (int i = 0; i < RunnerHelper.Size; i++)
yield return _array[i];
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
int _index = 0;
public void Reset() { _index = 0; }
public Item MoveNext() { return _array[_index++]; }
public bool EOF { get { return _index >= RunnerHelper.Size; } }
public bool ResetWithState() { _index = 0; return _index < RunnerHelper.Size; }
public bool MoveNextWithState()
{
_index++;
if (_index == RunnerHelper.Size)
return false;
return true;
}
public Item[] Results { get; set; }
public int ResultCount { get; set; }
public void Calculate()
{
ResultCount = 0;
for (int i = 0; i < RunnerHelper.Size; i++)
{
Results[i] = _array[i];
ResultCount++;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment