Skip to content

Report benchmark statistics and comparison #129

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 59 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
0ee13f5
feat: create rules source and middlewares support
luispfgarces Nov 27, 2022
91aa852
test: create benchmark project and add benchmarks
luispfgarces Oct 2, 2022
6cfe3b4
feat: add compilation functionality for value condition nodes
luispfgarces Oct 19, 2022
d254f78
feat: add various improvements to favor overral rules evaluation perf…
luispfgarces Oct 29, 2022
df63339
refactor: rename classic conditions eval engine
luispfgarces Oct 29, 2022
852ba1c
feat: implement remaining multiplicities and operators
luispfgarces Nov 5, 2022
452c044
test: add new unit tests covering compilation functionalities
luispfgarces Nov 5, 2022
545e031
refactor: implement additional performance improvements
luispfgarces Nov 15, 2022
ff9bd5c
test: test improvements
luispfgarces Nov 15, 2022
2adf807
refactor: optimize conditions convertion and extract conditions tree …
luispfgarces Nov 17, 2022
e01a85b
feat: move rules compilation to rules source middleware
luispfgarces Dec 1, 2022
1fa34c6
feat: add support for compiling whole rule conditions tree
luispfgarces Jan 7, 2023
a401ee0
fix: add missing package on unit tests and fix condition on compiled …
luispfgarces Jan 7, 2023
b21ed98
refactor: simplify and streamline rules compilation
luispfgarces Feb 19, 2023
00b42a3
docs: update expression builder flow documentation
luispfgarces Feb 19, 2023
401a0be
test: add the possibility to run only unit tests via script
luispfgarces Feb 19, 2023
264b738
test: add unit tests
luispfgarces Feb 19, 2023
c2e5e20
Merge branch 'master' into feat/49/allow_pre_compilation_of_rules_con…
luispfgarces Feb 19, 2023
7928cb4
test: add unit tests
luispfgarces Feb 19, 2023
6196eb4
refactor: self-review changes
luispfgarces Feb 19, 2023
faad592
chore: remove env file
luispfgarces Feb 19, 2023
3769675
chore: fix static analysis issues found by Codacy
luispfgarces Feb 21, 2023
c7bbb7e
chore: fix static analysis issues found by Codacy (2)
luispfgarces Feb 22, 2023
1ecbd00
chore: fix static analysis issues found by Codacy (3)
luispfgarces Feb 22, 2023
a3000e1
ci: add benchmark report results to Markdown via custom PS script
luispfgarces Mar 5, 2023
2d85a4d
chore: refactor benchmark report results generation
luispfgarces Mar 6, 2023
280d04a
chore: add new job to run benchmarks and publish results to github pr…
luispfgarces Mar 9, 2023
3e87b39
fix: change outputs notation
luispfgarces Mar 9, 2023
c6fa682
fix: resolve notation issue
luispfgarces Mar 9, 2023
78e195f
fix: resolve mispelled outputs
luispfgarces Mar 9, 2023
d32c3f5
fix: add missing git checkout action
luispfgarces Mar 9, 2023
e4da36d
fix: use dotnet command to run benchmarks
luispfgarces Mar 10, 2023
de98498
fix: publish benchmarks and run binary
luispfgarces Mar 10, 2023
c34d2d9
fix: change to run benchmark with privileges
luispfgarces Mar 11, 2023
e57aa96
Merge branch 'master' into feat/49/allow_pre_compilation_of_rules_con…
luispfgarces Mar 11, 2023
9510324
Merge branch 'master' into report_benchmark_statistics_and_comparison
luispfgarces Mar 11, 2023
1bbc1fc
chore: add list commands
luispfgarces Mar 12, 2023
ac19afb
Merge branch 'report_benchmark_statistics_and_comparison' of https://…
luispfgarces Mar 12, 2023
eb2da91
chore: run benchmarks with dotnet and dll
luispfgarces Mar 12, 2023
8afffcd
chore: run benchmarks as self-contained and remove bin folder
luispfgarces Mar 12, 2023
4d10a3e
chore: run benchmarks as framework dependent
luispfgarces Mar 12, 2023
b1c1f99
chore: change build & run benchmarks to run from csproj
luispfgarces Mar 12, 2023
ac7ee23
fix: add missing project switch
luispfgarces Mar 12, 2023
4f099b4
fix: add framework to run command
luispfgarces Mar 12, 2023
2c9c0c6
feat: produce benchmarks report on markdown format
luispfgarces Mar 12, 2023
ca1064b
Merge branch 'report_benchmark_statistics_and_comparison' of https://…
luispfgarces Mar 12, 2023
2131823
fix: fix null reference when there are statistics missing
luispfgarces Mar 12, 2023
059718c
chore: review file permissions and list all directories and sub-direc…
luispfgarces Mar 12, 2023
86cb300
chore: prefix paths from current folder
luispfgarces Mar 12, 2023
13592e0
chore: change to path of results
luispfgarces Mar 12, 2023
af42471
chore: consolidate change directory and determine report file
luispfgarces Mar 12, 2023
0258643
fix: set artifacts folder on chmod
luispfgarces Mar 12, 2023
8b6fe4f
fix: fix report results file path
luispfgarces Mar 12, 2023
87bcbd4
chore: change to use event number
luispfgarces Mar 12, 2023
e3e5aac
feat: changes to add/update a single comment with benchmarks results
luispfgarces Mar 13, 2023
86c18b3
chore: fix static code analysis issues and remove old logic for runni…
luispfgarces Mar 13, 2023
51985c6
Merge branch 'master' into report_benchmark_statistics_and_comparison
luispfgarces Apr 10, 2023
46cf199
fix: resolve bad conflicts from merge
luispfgarces Apr 10, 2023
52698a1
chore: minor fixes to files
luispfgarces Apr 10, 2023
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: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,7 @@ typings/
# Specific ones added
coverage-outputs/**
.env
tmp/**

### Rules Framework Web UI ###
!src/Rules.Framework.WebUI/node_modules/
Expand Down
55 changes: 55 additions & 0 deletions run-benchmarks.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
param ([switch] $KeepBenchmarksFiles)

$originalDir = (Get-Location).Path
$timestamp = [DateTime]::UtcNow.ToString("yyyyMMdd_hhmmss")

$directoryFound = Get-ChildItem -Path $originalDir -Directory | Select-String -Pattern "tmp"
if (!$directoryFound) {
New-Item -Name "tmp" -ItemType Directory > $null
}

$directoryFound = Get-ChildItem -Path "$originalDir\\tmp" -Directory | Select-String -Pattern "benchmarks"
if (!$directoryFound) {
New-Item -Name "benchmarks" -ItemType Directory -Path "$originalDir\\tmp" > $null
}

$directoryFound = Get-ChildItem -Path "$originalDir\\tmp\\benchmarks" -Directory | Select-String -Pattern $timestamp
if (!$directoryFound) {
New-Item -Name $timestamp -ItemType Directory -Path "$originalDir\\tmp\\benchmarks" > $null
}

$reportDir = "$originalDir\\tmp\\benchmarks\\$timestamp"

# Ensure all packages restored before running benchmarks
dotnet restore rules-framework.sln

# Build benchmarks binaries
dotnet build -c Release .\tests\Rules.Framework.BenchmarkTests\Rules.Framework.BenchmarkTests.csproj -o "$reportDir\\bin" --framework net6.0

Set-Location -Path $reportDir

# Run benchmarks
bin\Rules.Framework.BenchmarkTests.exe -a artifacts

# Determine results file
$filteredResultsFiles = Get-ChildItem -Path artifacts/results -File -Filter *.md
if ($filteredResultsFiles) {
$resultsFile = $filteredResultsFiles.Name

# Copy results file
Copy-Item -Path artifacts/results/$resultsFile -Destination .

# Rename file
Rename-Item -Path $resultsFile -NewName report.md
}

if (!$KeepBenchmarksFiles) {
if ($directoryFound = Get-ChildItem -Path $reportDir -Directory | Select-String -Pattern "artifacts") {
Remove-Item -Path artifacts -Recurse > $null
}
if ($directoryFound = Get-ChildItem -Path $reportDir -Directory | Select-String -Pattern "bin") {
Remove-Item -Path bin -Recurse > $null
}
}

Set-Location -Path $originalDir
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ namespace Rules.Framework.Core.ConditionNodes
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
namespace Rules.Framework.BenchmarkTests
{
using System;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;

internal class CustomBaselineClassifierColumn : IColumn
{
private readonly Func<BenchmarkCase, bool> classifyLogicFunc;

public CustomBaselineClassifierColumn(Func<BenchmarkCase, bool> classifyLogicFunc)
{
this.classifyLogicFunc = classifyLogicFunc;
}

public bool AlwaysShow => true;
public ColumnCategory Category => ColumnCategory.Baseline;
public string ColumnName => "Baseline";
public string Id => nameof(CustomBaselineClassifierColumn);
public bool IsNumeric => false;
public string Legend => "Sets wether a test case is a baseline.";
public int PriorityInCategory => 0;
public UnitType UnitType => UnitType.Dimensionless;

public string GetValue(Summary summary, BenchmarkCase benchmarkCase)
=> this.classifyLogicFunc.Invoke(benchmarkCase) ? "Yes" : "No";

public string GetValue(Summary summary, BenchmarkCase benchmarkCase, SummaryStyle style)
=> this.GetValue(summary, benchmarkCase);

public bool IsAvailable(Summary summary) => true;

public bool IsDefault(Summary summary, BenchmarkCase benchmarkCase) => false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Rules.Framework.BenchmarkTests.Exporters.Json
{
using System;

internal class BenchmarkReport
{
public DateTime Date { get; set; }

public Environment? Environment { get; set; }

public IEnumerable<BenchmarkStatisticsItem>? Statistics { get; set; }

public IEnumerable<BenchmarkStatisticsComparisonItem>? StatisticsComparison { get; set; }

public string Title { get; set; } = string.Empty;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Rules.Framework.BenchmarkTests.Exporters.Json
{
internal class BenchmarkStatisticsComparisonItem
{
public BenchmarkStatisticsValue? AllocatedMemoryRate { get; set; }

public BenchmarkStatisticsValue? BaselineAllocatedMemory { get; set; }

public BenchmarkStatisticsValue? BaselineMeanTimeTaken { get; set; }

public string BaselineParameters { get; set; } = string.Empty;

public BenchmarkStatisticsValue? CompareAllocatedMemory { get; set; }

public BenchmarkStatisticsValue? CompareMeanTimeTaken { get; set; }

public string CompareParameters { get; set; } = string.Empty;

public string Key { get; set; } = string.Empty;

public BenchmarkStatisticsValue? MeanTimeTakenCompareRate { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Rules.Framework.BenchmarkTests.Exporters.Json
{
internal class BenchmarkStatisticsItem
{
public BenchmarkStatisticsValue? AllocatedMemory { get; set; }

public string Baseline { get; set; } = string.Empty;

public BenchmarkStatisticsValue? BranchInstructionsPerOp { get; set; }

public BenchmarkStatisticsValue? BranchMispredictionsPerOp { get; set; }

public BenchmarkStatisticsValue? Gen0Collects { get; set; }

public string Key { get; set; } = string.Empty;

public BenchmarkStatisticsValue? MeanTimeTaken { get; set; }

public string Parameters { get; set; } = string.Empty;

public BenchmarkStatisticsValue? StandardError { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Rules.Framework.BenchmarkTests.Exporters.Json
{
internal class BenchmarkStatisticsValue
{
public string Format { get; set; } = string.Empty;

public string Unit { get; set; } = string.Empty;

public decimal Value { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
namespace Rules.Framework.BenchmarkTests.Exporters.Json
{
using System.Collections.Generic;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Reports;
using Newtonsoft.Json;

internal class CustomJsonExporter : ExporterBase
{
public CustomJsonExporter(bool indentJson)
{
this.IndentJson = indentJson;
}

public static IExporter Compressed => new CustomJsonExporter(indentJson: false);

public static IExporter Default => Indented;

public static IExporter Indented => new CustomJsonExporter(indentJson: true);

protected override string FileExtension => "json";

private bool IndentJson { get; }

public override void ExportToLog(Summary summary, ILogger logger)
{
var report = CustomJsonExporter.CreateReport(summary);

var settings = new JsonSerializerSettings
{
Formatting = Formatting.None
};

if (this.IndentJson)
{
settings.Formatting = Formatting.Indented;
}

string jsonText = JsonConvert.SerializeObject(report, settings);

logger.WriteLine(jsonText);
}

private static decimal CalculateRate(decimal baselineValue, decimal compareValue)
=> ((baselineValue - compareValue) / baselineValue) * 100;

private static BenchmarkStatisticsComparisonItem CreateBenchmarkStatisticsComparisonItem(BenchmarkStatisticsItem? baselineStatisticsItem, BenchmarkStatisticsItem? nonBaselineStatisticsItem) => new BenchmarkStatisticsComparisonItem
{
AllocatedMemoryRate = new BenchmarkStatisticsValue
{
Format = "0.##",
Unit = "%",
Value = CalculateRate(
baselineStatisticsItem.AllocatedMemory?.Value ?? 0,
nonBaselineStatisticsItem.AllocatedMemory?.Value ?? 0),
},
BaselineAllocatedMemory = baselineStatisticsItem.AllocatedMemory,
BaselineMeanTimeTaken = baselineStatisticsItem.MeanTimeTaken,
BaselineParameters = baselineStatisticsItem.Parameters,
CompareAllocatedMemory = nonBaselineStatisticsItem.AllocatedMemory,
CompareMeanTimeTaken = nonBaselineStatisticsItem.MeanTimeTaken,
CompareParameters = nonBaselineStatisticsItem.Parameters,
Key = baselineStatisticsItem.Key,
MeanTimeTakenCompareRate = new BenchmarkStatisticsValue
{
Format = "0.##",
Unit = "%",
Value = CalculateRate(
baselineStatisticsItem.MeanTimeTaken?.Value ?? 0,
nonBaselineStatisticsItem.MeanTimeTaken?.Value ?? 0),
}
};

private static BenchmarkStatisticsItem CreateBenchmarkStatisticsItem(SummaryTable.SummaryTableColumn baselineClassifierColumn, ref int current, BenchmarkDotNet.Reports.BenchmarkReport benchmarkReport)
{
var allocatedMemoryMetric = benchmarkReport.Metrics.FirstOrDefault(m => m.Key == "Allocated Memory");
var branchInstructionsMetric = benchmarkReport.Metrics.FirstOrDefault(m => m.Key == "BranchInstructions");
var branchMispredictionsMetric = benchmarkReport.Metrics.FirstOrDefault(m => m.Key == "BranchMispredictions");
var gen0CollectsMetric = benchmarkReport.Metrics.FirstOrDefault(m => m.Key == "Gen0Collects");

var statisticsItem = new BenchmarkStatisticsItem
{
AllocatedMemory = allocatedMemoryMetric.Key is null ? null : CreateBenchmarkStatisticsValue(allocatedMemoryMetric),
Baseline = baselineClassifierColumn.Content[current++],
BranchInstructionsPerOp =
branchInstructionsMetric.Key is null ? null : CreateBenchmarkStatisticsValue(branchInstructionsMetric),
BranchMispredictionsPerOp =
branchMispredictionsMetric.Key is null ? null : CreateBenchmarkStatisticsValue(branchMispredictionsMetric),
Gen0Collects = gen0CollectsMetric.Key is null ? null : CreateBenchmarkStatisticsValue(gen0CollectsMetric),
Key = $"{benchmarkReport.BenchmarkCase.Descriptor.Type.Name}.{benchmarkReport.BenchmarkCase.Descriptor.WorkloadMethod.Name}",
MeanTimeTaken = new BenchmarkStatisticsValue
{
Format = "N0",
Unit = "ns",
Value = Convert.ToDecimal(benchmarkReport.ResultStatistics?.Mean ?? 0),
},
Parameters = benchmarkReport.BenchmarkCase.Parameters.DisplayInfo,
StandardError = new BenchmarkStatisticsValue
{
Format = "N0",
Unit = "ns",
Value = Convert.ToDecimal(benchmarkReport.ResultStatistics?.StandardError ?? 0),
},
};
return statisticsItem;
}

private static BenchmarkStatisticsValue CreateBenchmarkStatisticsValue(KeyValuePair<string, Metric> allocatedMemoryMetric) => new BenchmarkStatisticsValue
{
Format = allocatedMemoryMetric.Value.Descriptor.NumberFormat,
Unit = allocatedMemoryMetric.Value.Descriptor.Unit,
Value = Convert.ToDecimal(allocatedMemoryMetric.Value.Value),
};

private static Environment CreateEnvironment(Summary summary) => new Environment
{
Architecture = summary.HostEnvironmentInfo.Architecture,
BenchmarkDotNetCaption = "BenchmarkDotNet",
BenchmarkDotNetVersion = summary.HostEnvironmentInfo.BenchmarkDotNetVersion,
BuildConfiguration = summary.HostEnvironmentInfo.Configuration,
DotNetCliVersion = summary.HostEnvironmentInfo.DotNetSdkVersion.Value,
DotNetRuntimeVersion = summary.HostEnvironmentInfo.RuntimeVersion,
LogicalCoreCount = summary.HostEnvironmentInfo.CpuInfo.Value.LogicalCoreCount.GetValueOrDefault(0),
PhysicalCoreCount = summary.HostEnvironmentInfo.CpuInfo.Value.PhysicalCoreCount.GetValueOrDefault(0),
ProcessorName = summary.HostEnvironmentInfo.CpuInfo.Value.ProcessorName,
};

private static BenchmarkReport CreateReport(Summary summary)
{
var report = new BenchmarkReport
{
Date = DateTime.UtcNow,
Environment = CreateEnvironment(summary),
Title = summary.Title,
};
var statistics = new List<BenchmarkStatisticsItem>(summary.Reports.Length);

var baselineClassifierColumn = summary.Table.Columns.First(c => c.OriginalColumn.ColumnName == "Baseline");
var current = 0;
foreach (var benchmarkReport in summary.Reports)
{
var statisticsItem = CreateBenchmarkStatisticsItem(baselineClassifierColumn, ref current, benchmarkReport);

statistics.Add(statisticsItem);
}
report.Statistics = statistics;

var baselineStatisticsItems = statistics.Where(i => string.Equals(i.Baseline, "Yes"));
var nonBaselineStatisticsItems = statistics.Where(i => string.Equals(i.Baseline, "No"));
var statisticsComparison = new List<BenchmarkStatisticsComparisonItem>();

foreach (var baselineStatisticsItem in baselineStatisticsItems)
{
foreach (var nonBaselineStatisticsItem in nonBaselineStatisticsItems.Where(i => string.Equals(i.Key, baselineStatisticsItem.Key)))
{
var statisticsComparisonItem = CreateBenchmarkStatisticsComparisonItem(baselineStatisticsItem, nonBaselineStatisticsItem);

statisticsComparison.Add(statisticsComparisonItem);
}
}
report.StatisticsComparison = statisticsComparison;

return report;
}
}
}
23 changes: 23 additions & 0 deletions tests/Rules.Framework.BenchmarkTests/Exporters/Json/Environment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Rules.Framework.BenchmarkTests.Exporters.Json
{
internal class Environment
{
public string Architecture { get; set; } = string.Empty;

public string BenchmarkDotNetCaption { get; set; } = string.Empty;

public string BenchmarkDotNetVersion { get; set; } = string.Empty;

public string BuildConfiguration { get; set; } = string.Empty;

public string DotNetCliVersion { get; set; } = string.Empty;

public string DotNetRuntimeVersion { get; set; } = string.Empty;

public int LogicalCoreCount { get; set; }

public int PhysicalCoreCount { get; set; }

public string ProcessorName { get; set; } = string.Empty;
}
}
Loading