Skip to content

Commit

Permalink
Add benchmark demo
Browse files Browse the repository at this point in the history
  • Loading branch information
MichalBrylka committed Sep 23, 2024
1 parent 3d21804 commit f55f176
Show file tree
Hide file tree
Showing 8 changed files with 413 additions and 1 deletion.
83 changes: 83 additions & 0 deletions Benchmarks/BestPractices/BoundsCheck.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System.Runtime.InteropServices;

namespace Benchmarks.BestPractices;

/*
| Method | Runtime | Mean | Error | StdDev | Ratio | Allocated |
|------------ |--------- |---------:|--------:|--------:|---------:|----------:|-
| Get | .NET 6.0 | 708.6 ns | 2.23 ns | 1.74 ns | baseline | - |
| UnsafeGet | .NET 6.0 | 940.6 ns | 3.42 ns | 3.20 ns | +33% | - |
| SpanGet | .NET 6.0 | 432.0 ns | 5.55 ns | 5.19 ns | -39% | - |
| SpanListGet | .NET 6.0 | 426.9 ns | 1.05 ns | 0.93 ns | -40% | - |
| GetReverse | .NET 6.0 | 709.4 ns | 3.26 ns | 2.72 ns | +0% | - |
| | | | | | | |
| Get | .NET 8.0 | 477.2 ns | 7.30 ns | 6.47 ns | baseline | - |
| UnsafeGet | .NET 8.0 | 427.1 ns | 0.80 ns | 0.62 ns | -11% | - |
| SpanGet | .NET 8.0 | 297.4 ns | 2.26 ns | 1.89 ns | -38% | - |
| SpanListGet | .NET 8.0 | 299.0 ns | 3.41 ns | 3.02 ns | -37% | - |
| GetReverse | .NET 8.0 | 355.5 ns | 1.50 ns | 1.25 ns | -26% | - |
*/
public class BoundsCheck
{
private const int SIZE = 1000;
private static readonly int[] _array = Enumerable.Range(0, SIZE).ToArray();
private static readonly List<int> _list = Enumerable.Range(0, SIZE).ToList();

[Benchmark(Baseline = true)]
public int Get()
{
int x = 0;
for (int i = 0; i < SIZE; i++)
{
x = _array[i];
}
return x;
}

[Benchmark]
public int UnsafeGet()
{
int x = 0;
for (int i = 0; i < SIZE; i++)
{
x = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_array), i);
}
return x;
}

[Benchmark]
public int SpanGet()
{
var span = _array.AsSpan();
int x = 0;
for (int i = 0; i < SIZE; i++)
{
x = span[i];
}
return x;
}

[Benchmark]
public int SpanListGet()
{
var span = CollectionsMarshal.AsSpan(_list);
int x = 0;
for (int i = 0; i < SIZE; i++)
{
x = span[i];
}
return x;
}

[Benchmark]
public int GetReverse()
{
int x = 0;
for (int i = SIZE - 1; i >= 0; i--)
{
x = _array[i];
}

return x;
}
}
41 changes: 41 additions & 0 deletions Benchmarks/BestPractices/Boxing.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace Benchmarks.BestPractices;

/*
| Method | Runtime | Mean | Error | StdDev | Ratio | Gen0 | Allocated |
|------- |--------- |-----------:|---------:|----------:|---------:|-------:|----------:|
| Boxing | .NET 6.0 | 2,881.4 ns | 55.29 ns | 71.89 ns | baseline | 3.8261 | 24024 B |
| Int | .NET 6.0 | 244.8 ns | 3.10 ns | 2.75 ns | -92% | 0.0038 | 24 B |
| | | | | | | | |
| Boxing | .NET 8.0 | 2,946.5 ns | 56.35 ns | 155.20 ns | baseline | 3.8261 | 24024 B |
| Int | .NET 8.0 | 248.8 ns | 4.66 ns | 4.13 ns | -92% | 0.0038 | 24 B |
*/
public class BoxingBench
{
private const int SIZE = 1000;

[Benchmark(Baseline = true)]
public object Boxing()
{
int x = 0;
object obj = x;
for (int i = 0; i < SIZE; i++)
{
x++;
obj = x;
}
return obj;
}


[Benchmark]
public object Int()
{
int x = 0;
object obj = x;
for (int i = 0; i < SIZE; i++)
{
x++;
}
return obj;
}
}
38 changes: 38 additions & 0 deletions Benchmarks/BestPractices/BoxingUnsafe.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
namespace Benchmarks.BestPractices;

/*
| Method | Runtime | Mean | Error | StdDev | Ratio | Gen0 | Allocated |
|--------------- |--------- |---------:|----------:|----------:|---------:|-------:|----------:|-
| UnBoxing | .NET 6.0 | 2.931 us | 0.0568 us | 0.0584 us | baseline | 3.8261 | 24024 B |
| UnBoxingUnsafe | .NET 6.0 | 1.248 us | 0.0046 us | 0.0041 us | -57% | 0.0038 | 24 B |
| | | | | | | | |
| UnBoxing | .NET 8.0 | 2.961 us | 0.0547 us | 0.0427 us | baseline | 3.8261 | 24024 B |
| UnBoxingUnsafe | .NET 8.0 | 1.236 us | 0.0235 us | 0.0289 us | -58% | 0.0038 | 24 B |
*/
public class BoxingUnsafe
{
private const int SIZE = 1000;

[Benchmark(Baseline = true)]
public object UnBoxing()
{
object obj = 0;
for (int i = 0; i < SIZE; i++)
Inc((int)obj);

return obj;

static object Inc(int x) => ++x;
}


[Benchmark]
public object UnBoxingUnsafe()
{
object obj = 0;
for (int i = 0; i < SIZE; i++)
Unsafe.Unbox<int>(obj)++;

return obj;
}
}
43 changes: 43 additions & 0 deletions Benchmarks/BestPractices/CodeGen.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
namespace Benchmarks.BestPractices;
internal class CodeGen
{
static bool IsEven(int i) => (i % 2) == 0;
}


/*
mono:
Program:IsEven(System.Int32):System.Boolean:
sub rsp, 8
mov [rsp], r15
mov r15, rdi
mov rcx, r15
shr ecx, 1Fh
mov rax, r15
add eax, ecx
and rax, 1
sub eax, ecx
test eax, eax
sete al
movzx rax, al
mov r15, [rsp]
add rsp, 8
ret
6.0
Program:IsEven(int):bool:
test dil, 1
sete al
movzx rax, al
ret
8.0
Program:IsEven(int):bool (FullOpts):
G_M20272_IG01: ;; offset=0x0000
G_M20272_IG02: ;; offset=0x0000
mov eax, edi
not eax
and eax, 1
G_M20272_IG03: ;; offset=0x0007
ret
*/
31 changes: 31 additions & 0 deletions Benchmarks/BestPractices/Demo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[Start](https://benchmarkdotnet.org/)
[Getting Started Guide](https://benchmarkdotnet.org/articles/guides/getting-started.html)
[Main Features List](https://benchmarkdotnet.org/#main-features)



# Running
[Config](https://benchmarkdotnet.org/articles/configs/configs.html)

```csharp
var config = ManualConfig
.Create(DefaultConfig.Instance)
.AddValidator(ExecutionValidator.FailOnError)
.WithSummaryStyle(SummaryStyle.Default.WithRatioStyle(RatioStyle.Percentage))
.AddDiagnoser(MemoryDiagnoser.Default)
.HideColumns("Median", "RatioSD", "Alloc Ratio") //"Error", "StdDev",
.AddJob(Job.Default.WithRuntime(CoreRuntime.Core60))
.AddJob(Job.Default.WithRuntime(CoreRuntime.Core80))
;
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config);
```


# Results
1. [Exporters](https://benchmarkdotnet.org/articles/configs/exporters.html)
2. [Charting](https://chartbenchmark.net/)
3. [Plot for blogs](https://michalbrylka.github.io/posts/generic-math-matrix/#performance)


# Utils
1. [BenchmarkInput.cs](Helpers/BenchmarkInput.cs)
142 changes: 142 additions & 0 deletions Benchmarks/BestPractices/GeneratorBench.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
namespace Benchmarks.BestPractices;

/*
| Method | Runtime | Mean | Ratio |
|----------------- |--------- |-----------:|---------:|
| Yield | .NET 6.0 | 3,778.5 ns | baseline |
| YieldLocalMethod | .NET 6.0 | 3,760.8 ns | -0% |
| EnumerableRange | .NET 6.0 | 3,536.0 ns | -6% |
| ArrayBench | .NET 6.0 | 850.7 ns | -77% |
| | | | |
| Yield | .NET 8.0 | 1,452.5 ns | baseline |
| YieldLocalMethod | .NET 8.0 | 1,440.6 ns | -1% |
| EnumerableRange | .NET 8.0 | 1,432.5 ns | -1% |
| ArrayBench | .NET 8.0 | 753.2 ns | -48% |
*/
public class GeneratorBench
{
private const int SIZE = 1000;

[Benchmark(Baseline = true)]
public int Yield()
{
int sum = 0;
foreach (var i in Sequence())
sum += i;
return sum;
}

static IEnumerable<int> Sequence()
{
for (int i = 0; i < SIZE; i++)
yield return i;
}

[Benchmark]
public int YieldLocalMethod()
{
int sum = 0;
foreach (var i in SequenceLocal())
sum += i;
return sum;

static IEnumerable<int> SequenceLocal()
{
for (int i = 0; i < SIZE; i++)
yield return i;
}
}

[Benchmark]
public int EnumerableRange()
{
int sum = 0;
foreach (var i in Enumerable.Range(0, SIZE))
sum += i;
return sum;
}

[Benchmark]
public int ArrayBench()
{
int sum = 0;
foreach (var i in ArrayGen())
sum += i;
return sum;
}


static int[] ArrayGen()
{
var arr = new int[SIZE];
for (int i = 0; i < SIZE; i++)
arr[i] = i;
return arr;
}
}





















































/*
| Method | Runtime | Mean | Ratio | Gen0 | Allocated |
|----------------- |--------- |-----------:|---------:|-------:|----------:|-
| Yield | .NET 6.0 | 3,778.5 ns | baseline | - | 32 B |
| YieldLocalMethod | .NET 6.0 | 3,760.8 ns | -0% | 0.0038 | 32 B |
| EnumerableRange | .NET 6.0 | 3,536.0 ns | -6% | 0.0038 | 40 B |
| ArrayBench | .NET 6.0 | 850.7 ns | -77% | 0.6409 | 4024 B |
| | | | | | |
| Yield | .NET 8.0 | 1,452.5 ns | baseline | 0.0038 | 32 B |
| YieldLocalMethod | .NET 8.0 | 1,440.6 ns | -1% | 0.0038 | 32 B |
| EnumerableRange | .NET 8.0 | 1,432.5 ns | -1% | 0.0057 | 40 B |
| ArrayBench | .NET 8.0 | 753.2 ns | -48% | 0.6409 | 4024 B |
*/
Loading

0 comments on commit f55f176

Please sign in to comment.