Skip to content

Commit

Permalink
Benchmark improvements (#81)
Browse files Browse the repository at this point in the history
This change adds additional benchmarks along with some scripts to make
them easier to run, display, and combine the output.
  • Loading branch information
craigktreasure authored Nov 17, 2023
1 parent a93f99e commit 2379fbb
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 6 deletions.
107 changes: 107 additions & 0 deletions test/Treasure.Utils.Argument.Benchmarks/Combine.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#Requires -PSEdition Core

[CmdletBinding()]
Param (
[string[]] $TFMs = @('net6.0', 'net6.0-windows', 'net7.0', 'net7.0-windows'),
[string[]] $Columns = @( 'Runtime', 'TFM', 'LibraryTfm', 'Method', 'Mean', 'Error', 'StdDev', 'Code Size'),
[string] $ArtifactInputRoot = (Join-Path $PSScriptRoot '..' '..' '__benchmarks'),
[string] $ResultOutputPath = (Join-Path $PSScriptRoot '..' '..' '__benchmarks' 'results.md'),
[string[]] $MethodSortOrder = @(
'ArgumentNotNull',
'ArgumentNullExceptionThrowIfNull',
'IfValueIsNullThrowArgumentNullException',
'ArgumentNotNullOrEmpty',
'ArgumentExceptionThrowIfNullOrEmpty',
'IfValueIsNullOrEmptyThrowArgumentException',
'ArgumentNotNullOrWhiteSpace',
'ArgumentExceptionThrowIfNullOrWhiteSpace',
'IfValueIsNullOrWhiteSpaceThrowArgumentException')
)

$ErrorActionPreference = 'Stop'
Set-StrictMode -Version 2

function BuildTableRowFromData ($rowData) {
$row = '|'

foreach ($data in $rowData) {
$row += " $data |"
}

return $row
}

function BuildTableRowFromColumnsAndData ($columns, $data) {
BuildTableRowFromData ($columns | ForEach-Object { $data.$_ })
}

function GetLibraryTfmName ($tfm) {
if ($tfm -eq 'net6.0') {
return 'net6.0'
}

if ($tfm -eq 'net7.0') {
return 'net7.0'
}

if ($tfm.Contains('-')) {
return 'netstandard2.1'
}

throw "Unknown TFM '$tfm'."
}

function GetLibraryTfmValue ($data, $libraryTfm) {
if ($data.Method.Contains('Exception')) {
return 'NA'
}

return $libraryTfm
}

if (!(Test-Path $ArtifactInputRoot)) {
Write-Error "Artifact input root path '$ArtifactInputRoot' does not exist."
}

$csvData = @()
$TFMs
| ForEach-Object {
[PSCustomObject] @{
libraryTfm = (GetLibraryTfmName $_)
tfm = $_
path = (Join-Path $ArtifactInputRoot "$_.artifacts/results/Tests-report.csv")
}
}
| Where-Object { Test-Path $_.path }
| ForEach-Object {
$libraryTfm = $_.libraryTfm
$tfm = $_.tfm
Import-Csv -Path $_.path
| ForEach-Object {
$_ | Add-Member -NotePropertyName 'LibraryTfm' -NotePropertyValue (GetLibraryTfmValue $_ $libraryTfm) -PassThru
| Add-Member -NotePropertyName 'TFM' -NotePropertyValue $tfm -PassThru
}
}
| ForEach-Object { $csvData += $_ }
$csvData = $csvData | Sort-Object { $MethodSortOrder.IndexOf($_.Method) }, Runtime, LibraryTfm, TFM

# Write header to markdown file
Set-Content -Path $ResultOutputPath -Value '# Results'
Add-Content -Path $ResultOutputPath -Value ''
Add-Content -Path $ResultOutputPath -Value (BuildTableRowFromData $Columns)
Add-Content -Path $ResultOutputPath -Value (BuildTableRowFromData ($Columns | ForEach-Object { '-'*$_.Length }))

# Write to markdown file
$emptyRow = BuildTableRowFromData ($Columns | ForEach-Object { '' } )
$lastMethod = $csvData[0].Method
$csvData | ForEach-Object {
$method = $_.Method

if ($method -ne $lastMethod) {
Add-Content -Path $ResultOutputPath -Value $emptyRow
$lastMethod = $method
}

$mdLine = BuildTableRowFromColumnsAndData $Columns $_
Add-Content -Path $ResultOutputPath -Value $mdLine
}
89 changes: 83 additions & 6 deletions test/Treasure.Utils.Argument.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
@@ -1,31 +1,108 @@
// dotnet run -c Release -f net6.0 --filter "*" --runtimes net6.0 net7.0
// .\Run.ps1
// or
// dotnet run -c Release -f [net6.0 net7.0] --filter "*"

#pragma warning disable IDE0211
#pragma warning disable CA1050
#pragma warning disable CA1812
#pragma warning disable CA1822
#pragma warning disable CA1510
#pragma warning disable CS1591
#pragma warning disable IDE0211

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

using Treasure.Utils;

BenchmarkSwitcher.FromAssembly(typeof(Tests).Assembly).Run(args);
BenchmarkRunner.Run<Tests>(args: args);

[HideColumns("value")]
[DisassemblyDiagnoser]
public class Tests
{
[Benchmark]
[Arguments("foo")]
public void NotNull(string? value) => Argument.NotNull(value);
public void ArgumentNotNull(string? value)
{
Argument.NotNull(value);
}

[Benchmark]
[Arguments("foo")]
public void ArgumentNullExceptionThrowIfNull(string? value)
{
ArgumentNullException.ThrowIfNull(value);
}

[Benchmark]
[Arguments("foo")]
public void IfValueIsNullThrowArgumentNullException(string? value)
{
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}
}

[Benchmark]
[Arguments("foo")]
public void ArgumentNotNullOrEmpty(string? value)
{
Argument.NotNullOrEmpty(value);
}

#if NET7_0_OR_GREATER
[Benchmark]
[Arguments("foo")]
public void NotNullOrEmpty(string? value) => Argument.NotNullOrEmpty(value);
public void ArgumentExceptionThrowIfNullOrEmpty(string? value)
{
ArgumentException.ThrowIfNullOrEmpty(value);
}
#endif

[Benchmark]
[Arguments("foo")]
public void NotNullOrWhiteSpace(string? value) => Argument.NotNullOrWhiteSpace(value);
public void IfValueIsNullOrEmptyThrowArgumentException(string? value)
{
if (value is null)
{
throw new ArgumentNullException(value);
}

if (string.IsNullOrEmpty(value))
{
throw new ArgumentException("Value cannot be null or empty.", nameof(value));
}
}

[Benchmark]
[Arguments("foo")]
public void ArgumentNotNullOrWhiteSpace(string? value)
{
Argument.NotNullOrWhiteSpace(value);
}

#if NET8_0_OR_GREATER
[Benchmark]
[Arguments("foo")]
public void ArgumentExceptionThrowIfNullOrWhiteSpace(string? value)
{
ArgumentException.ThrowIfNullOrWhiteSpace(value);
}
#endif

[Benchmark]
[Arguments("foo")]
public void IfValueIsNullOrWhiteSpaceThrowArgumentException(string? value)
{
if (value is null)
{
throw new ArgumentNullException(value);
}

if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("Value cannot be null or empty.", nameof(value));
}
}
}
25 changes: 25 additions & 0 deletions test/Treasure.Utils.Argument.Benchmarks/Run.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#Requires -PSEdition Core

[CmdletBinding()]
Param (
[string[]] $TFMs = @('net6.0', 'net6.0-windows', 'net7.0', 'net7.0-windows'),
[string] $ArtifactOutputRoot = (Join-Path $PSScriptRoot '..' '..' '__benchmarks'),
[string] $ResultOutputPath = (Join-Path $PSScriptRoot '..' '..' '__benchmarks' 'results.md')
)

$ErrorActionPreference = 'Stop'
Set-StrictMode -Version 2

Push-Location $PSScriptRoot
try {
foreach ($tfm in $TFMs) {
$tfmBenchmarkOutput = Join-Path $ArtifactOutputRoot "$tfm.artifacts"
dotnet run -c Release -f $tfm --filter "*" -- -a $tfmBenchmarkOutput
}
}
finally {
Pop-Location
}

$combineScriptPath = Join-Path $PSScriptRoot 'Combine.ps1'
. $combineScriptPath -TFMs $TFMs -ArtifactInputRoot $ArtifactOutputRoot -ResultOutputPath $ResultOutputPath

0 comments on commit 2379fbb

Please sign in to comment.