Welcome to the repository accompanying my conference talk "A Season for Speed: Turning Puzzles into C# Performance Win". This project showcases my journey of solving Advent of Code puzzles while learning and applying C# performance optimization techniques.
Get the slides I used at previous presentations.
Ensure you have the following installed:
For most benchmarks:
- .NET 8.0 SDK
For the cross-framework comparison benchmark:
- .NET 8.0 SDK
- .NET 7.0 SDK
- .NET 6.0 SDK
- .NET 5.0 SDK
- .NET Framework 4.8.1 SDK
git clone https://github.com/eNeRGy164/AdventOfPerformance
cd AdventOfPerformance
For running with the Native AOT, as I did for my talk, use:
dotnet run -c Release -f net8.0 --framework nativeaot8.0 --filter *
This takes about 16 minutes on an AMD Ryzen 5 7600.
Important
On processors with the AVX-512F
capability, the current version of BenchmarkDotNet (0.12.3
) may generate invalid code and not work correctly.
This issue will be resolved in the next version of BenchmarkDotNet.
To run all the benchmarks, use the following command:
dotnet run -c Release -f net8.0 --filter *
This takes about 16 minutes on an AMD Ryzen 5 7600.
To run all the benchmarks, use the following command:
dotnet run -c Release -f net8.0 --framework nativeaot8.0 --filter AdventOfPerformance.Puzzle*
This takes about 16 minutes on an AMD Ryzen 5 7600.
dotnet run -c Release -f net8.0 --filter AdventOfPerformance.Framework*
This takes about 1.5 minutes on an AMD Ryzen 5 7600.
BenchmarkDotNet v0.13.13-nightly.20240601.156, Windows 11 (10.0.22631.3737/23H2/2023Update/SunValley3)
AMD Ryzen 5 7600, 1 CPU, 12 logical and 6 physical cores
.NET SDK 8.0.302
[Host] : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
Job-MUAWXP : .NET 8.0.6, X64 NativeAOT AVX-512F+CD+BW+DQ+VL+VBMI
Runtime=NativeAOT 8.0 Toolchain=Latest ILCompiler
Method | Median | Ratio | Gen0 | Gen1 | Allocated | Alloc Ratio |
---|---|---|---|---|---|---|
LinqQuery | 528,551.5 ns | 1.000 | 62.5000 | - | 1048328 B | 1.000 |
LinqQueryIndex | 420,219.8 ns | 0.792 | 49.3164 | - | 830576 B | 0.792 |
Foreach | 185,389.9 ns | 0.349 | 0.4883 | - | 10472 B | 0.010 |
ForeachInnerRange | 13,049.5 ns | 0.025 | 5.1575 | 0.0153 | 86472 B | 0.082 |
For | 10,302.7 ns | 0.019 | 0.0458 | - | 872 B | 0.001 |
Pointers | 6,196.6 ns | 0.012 | - | - | - | 0.000 |
IntermediateVariables | 6,055.8 ns | 0.011 | 0.0458 | - | 872 B | 0.001 |
ForGoto | 5,920.1 ns | 0.011 | 0.0458 | - | 872 B | 0.001 |
Refs | 5,567.1 ns | 0.011 | 0.0458 | - | 824 B | 0.001 |
InitializeArray | 4,394.5 ns | 0.008 | 0.0458 | - | 824 B | 0.001 |
CustomIntParser | 3,519.3 ns | 0.007 | 0.0458 | - | 824 B | 0.001 |
LowerMemory | 3,494.3 ns | 0.007 | 0.0229 | - | 424 B | 0.000 |
TwoPointer | 3,010.2 ns | 0.006 | 0.0496 | - | 872 B | 0.001 |
Hashing | 1,923.7 ns | 0.004 | 0.2327 | - | 3944 B | 0.004 |
Bits | 245.4 ns | 0.000 | 0.0186 | - | 312 B | 0.000 |
BenchmarkDotNet v0.13.13-nightly.20240601.156, Windows 11 (10.0.22631.3737/23H2/2023Update/SunValley3)
AMD Ryzen 5 7600, 1 CPU, 12 logical and 6 physical cores
.NET SDK 8.0.302
[Host] : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
Job-MUAWXP : .NET 8.0.6, X64 NativeAOT AVX-512F+CD+BW+DQ+VL+VBMI
Runtime=NativeAOT 8.0 Toolchain=Latest ILCompiler
Method | Median | Ratio | Gen0 | Allocated | Alloc Ratio |
---|---|---|---|---|---|
LinqQuery | 18,929.987 μs | 1.000 | 2156.2500 | 36410868 B | 1.000 |
CustomIntParser | 63.120 μs | 0.003 | - | 824 B | 0.000 |
TwoPointer | 3.020 μs | 0.000 | 0.0496 | 872 B | 0.000 |
BenchmarkDotNet v0.13.13-nightly.20240601.156, Windows 11 (10.0.22631.3737/23H2/2023Update/SunValley3)
AMD Ryzen 5 7600, 1 CPU, 12 logical and 6 physical cores
.NET SDK 8.0.302
[Host] : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
.NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9241.0), X64 RyuJIT VectorSize=256
.NET 5.0 : .NET 5.0.17 (5.0.1722.21314), X64 RyuJIT AVX2
.NET 7.0 : .NET 7.0.20 (7.0.2024.26716), X64 RyuJIT AVX2
.NET 6.0 : .NET 6.0.31 (6.0.3124.26714), X64 RyuJIT AVX2
.NET 8.0 : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
Method | Job | Runtime | Median | Gen0 | Allocated |
---|---|---|---|---|---|
LinqQuery | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 994.5 μs | 166.0156 | 1026.81 KB |
LinqQuery | .NET 5.0 | .NET 5.0 | 491.0 μs | 62.5000 | 1023.76 KB |
LinqQuery | .NET 7.0 | .NET 7.0 | 440.3 μs | 62.5000 | 1023.76 KB |
LinqQuery | .NET 6.0 | .NET 6.0 | 437.1 μs | 62.5000 | 1023.76 KB |
LinqQuery | .NET 8.0 | .NET 8.0 | 294.1 μs | 62.5000 | 1023.76 KB |
WalkThroughMultiDimArraysBenchmarks.cs
BenchmarkDotNet v0.13.13-nightly.20240601.156, Windows 11 (10.0.22631.3737/23H2/2023Update/SunValley3)
AMD Ryzen 5 7600, 1 CPU, 12 logical and 6 physical cores
.NET SDK 8.0.302
[Host] : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
Job-MUAWXP : .NET 8.0.6, X64 NativeAOT AVX-512F+CD+BW+DQ+VL+VBMI
Runtime=NativeAOT 8.0 Toolchain=Latest ILCompiler
Method | Median | Allocated |
---|---|---|
'Walk through 2D Array, column by column' | 233.8 μs | - |
'Walk through 2D Array, row by row' | 138.7 μs | - |
'Use enumerator of the 2D Array' | 122.9 μs | - |
SerializeMultiDimArraysBenchmarks.cs
BenchmarkDotNet v0.13.13-nightly.20240601.156, Windows 11 (10.0.22631.3737/23H2/2023Update/SunValley3)
AMD Ryzen 5 7600, 1 CPU, 12 logical and 6 physical cores
.NET SDK 8.0.302
[Host] : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
Job-MUAWXP : .NET 8.0.6, X64 NativeAOT AVX-512F+CD+BW+DQ+VL+VBMI
Runtime=NativeAOT 8.0 Toolchain=Latest ILCompiler
Method | Median | Gen0 | Gen1 | Gen2 | Allocated |
---|---|---|---|---|---|
WriteCharYX | 636.8 μs | 166.0156 | 166.0156 | 166.0156 | 1031.03 KB |
WriteCharForeach | 584.4 μs | 166.0156 | 166.0156 | 166.0156 | 1031.03 KB |
WriteCharStringCreate | 403.1 μs | 166.5039 | 166.5039 | 166.5039 | 512.08 KB |
WriteCharSpan | 195.2 μs | 166.5039 | 166.5039 | 166.5039 | 512.08 KB |
BenchmarkDotNet v0.13.13-nightly.20240601.156, Windows 11 (10.0.22631.3737/23H2/2023Update/SunValley3)
AMD Ryzen 5 7600, 1 CPU, 12 logical and 6 physical cores
.NET SDK 8.0.302
[Host] : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
Job-MUAWXP : .NET 8.0.6, X64 NativeAOT AVX-512F+CD+BW+DQ+VL+VBMI
Runtime=NativeAOT 8.0 Toolchain=Latest ILCompiler
Method | Median | Ratio | Gen0 | Gen1 | Allocated | Alloc Ratio |
---|---|---|---|---|---|---|
Regex | 484.23 μs | 1.00 | 103.0273 | 12.6953 | 1683.3 KB | 1.00 |
RegexGenerated | 343.51 μs | 0.71 | 103.0273 | 12.6953 | 1683.3 KB | 1.00 |
CharSplit | 236.39 μs | 0.49 | 51.2695 | 6.3477 | 840.38 KB | 0.50 |
CharsSplit | 137.35 μs | 0.28 | 34.1797 | 4.1504 | 560.55 KB | 0.33 |
Spans | 32.04 μs | 0.07 | 2.1362 | - | 35.11 KB | 0.02 |
SpansAnalyzeInput | 10.69 μs | 0.02 | 2.1362 | - | 35.11 KB | 0.02 |
CreateObjectsInHashSetBenchmark.cs
BenchmarkDotNet v0.13.13-nightly.20240601.156, Windows 11 (10.0.22631.3737/23H2/2023Update/SunValley3)
AMD Ryzen 5 7600, 1 CPU, 12 logical and 6 physical cores
.NET SDK 8.0.302
[Host] : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
Job-MUAWXP : .NET 8.0.6, X64 NativeAOT AVX-512F+CD+BW+DQ+VL+VBMI
Runtime=NativeAOT 8.0 Toolchain=Latest ILCompiler
Method | Median | Ratio | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio |
---|---|---|---|---|---|---|---|
'Class w/ properties assigned in ctor' | 139.1 μs | 1.00 | 49.8047 | 49.8047 | 49.8047 | 314.64 KB | 1.00 |
'Struct w/ readonly fields assigned in ctor' | 136.5 μs | 0.98 | 49.8047 | 49.8047 | 49.8047 | 314.92 KB | 1.00 |
'Readonly Struct w/ properties assigned in ctor' | 135.6 μs | 0.97 | 49.8047 | 49.8047 | 49.8047 | 314.92 KB | 1.00 |
'Struct w/ properties assigned in the ctor' | 135.1 μs | 0.97 | 49.8047 | 49.8047 | 49.8047 | 314.92 KB | 1.00 |
'Sealed Class w/ properties assigned in ctor' | 134.5 μs | 0.96 | 49.8047 | 49.8047 | 49.8047 | 314.64 KB | 1.00 |
'Class w/ fields assigned in the ctor' | 133.6 μs | 0.96 | 49.8047 | 49.8047 | 49.8047 | 314.64 KB | 1.00 |
'Readonly Struct w/ '[in]' properties assigned in ctor' | 133.7 μs | 0.96 | 49.8047 | 49.8047 | 49.8047 | 314.92 KB | 1.00 |
'Record Class w/ properties assigned via primary ctor' | 127.3 μs | 0.91 | 49.8047 | 49.8047 | 49.8047 | 314.64 KB | 1.00 |
'Sealed Record Class w/ properties assigned via the primary ctor' | 127.0 μs | 0.91 | 49.8047 | 49.8047 | 49.8047 | 314.64 KB | 1.00 |
'Readonly Record Class w/ '[in]' properties assigned via primary ctor' | 104.3 μs | 0.75 | 49.9268 | 49.9268 | 49.9268 | 197.45 KB | 0.63 |
'Readonly Record Struct w/ properties assigned via primary ctor' | 103.6 μs | 0.74 | 49.9268 | 49.9268 | 49.9268 | 197.45 KB | 0.63 |
'Record Struct w/ properties assigned via primary ctor' | 103.6 μs | 0.74 | 49.9268 | 49.9268 | 49.9268 | 197.45 KB | 0.63 |
Feel free to submit issues or pull requests if you have suggestions or improvements.
This project is licensed under the MIT License - see the LICENSE file for details.