Last active
June 16, 2020 21:47
-
-
Save mgravell/0f728b919d40d718b504c5144a9833f9 to your computer and use it in GitHub Desktop.
Compare Span vs params array performance
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using BenchmarkDotNet.Attributes; | |
using BenchmarkDotNet.Running; | |
using BenchmarkDotNet.Configs; | |
using BenchmarkDotNet.Jobs; | |
using MemoryDiagnoser = BenchmarkDotNet.Diagnosers.MemoryDiagnoser; | |
using BenchmarkDotNet.Validators; | |
using BenchmarkDotNet.Columns; | |
using System.Runtime.CompilerServices; | |
namespace Params | |
{ | |
public class Program | |
{ | |
static void Main() | |
{ | |
// tell BenchmarkDotNet not to force GC.Collect after benchmark iteration | |
// (single iteration contains of multiple (usually millions) of invocations) | |
// it can influence the allocation-heavy Task<T> benchmarks | |
var gcMode = new GcMode { Force = false }; | |
var customConfig = ManualConfig | |
.Create(DefaultConfig.Instance) // copies all exporters, loggers and basic stuff | |
.With(JitOptimizationsValidator.FailOnError) // Fail if not release mode | |
.With(MemoryDiagnoser.Default) // use memory diagnoser | |
.With(StatisticColumn.OperationsPerSecond) // add ops/s | |
.With(Job.Default.With(gcMode)); | |
#if NET462 | |
// enable the Inlining Diagnoser to find out what does not get inlined | |
// uncomment it first, it produces a lot of output | |
//customConfig = customConfig.With(new BenchmarkDotNet.Diagnostics.Windows.InliningDiagnoser(logFailuresOnly: true, filterByNamespace: true)); | |
#endif | |
var summary = BenchmarkRunner.Run<Program>(customConfig); | |
Console.WriteLine(summary); | |
} | |
const int REPEATS_PER_ITEM = 1024; | |
[Benchmark(OperationsPerInvoke = REPEATS_PER_ITEM)] | |
public int ParamsArray() | |
{ | |
int total = 0; | |
for(int i = 0 ; i < REPEATS_PER_ITEM ; i++) | |
{ | |
total += Execute(1,2,3,4,5); | |
} | |
return total; | |
} | |
[Benchmark(OperationsPerInvoke = REPEATS_PER_ITEM)] | |
public int ExplicitArray() | |
{ | |
int total = 0; | |
for(int i = 0 ; i < REPEATS_PER_ITEM ; i++) | |
{ | |
total += Execute(new int[] {1,2,3,4,5}); | |
} | |
return total; | |
} | |
[Benchmark(OperationsPerInvoke = REPEATS_PER_ITEM)] | |
public unsafe int Span() | |
{ | |
int total = 0; | |
for(int i = 0 ; i < REPEATS_PER_ITEM ; i++) | |
{ | |
var args = stackalloc int[5]; | |
args[0] = 1; | |
args[1] = 2; | |
args[2] = 3; | |
args[3] = 4; | |
args[4] = 5; | |
total += Execute(new Span<int>(args, 5)); | |
} | |
return total; | |
} | |
[Benchmark(OperationsPerInvoke = REPEATS_PER_ITEM)] | |
public unsafe int SpanTuple() | |
{ | |
int total = 0; | |
for(int i = 0 ; i < REPEATS_PER_ITEM ; i++) | |
{ | |
var args = (1,2,3,4,5); | |
total += Execute(new Span<int>(Unsafe.AsPointer(ref args.Item1), 5)); | |
} | |
return total; | |
} | |
[Benchmark(OperationsPerInvoke = REPEATS_PER_ITEM)] | |
public int StaticBackedSpan() | |
{ | |
int total = 0; | |
for(int i = 0 ; i < REPEATS_PER_ITEM ; i++) | |
{ | |
total += Execute(new ReadOnlySpan<int>(__backingField | |
?? (__backingField = new int[] {1,2,3,4,5}))); | |
} | |
return total; | |
} | |
static int[] __backingField; | |
[MethodImpl(MethodImplOptions.NoInlining)] | |
int Execute(params int[] items) => items.Length; | |
[MethodImpl(MethodImplOptions.NoInlining)] | |
int Execute(Span<int> items) => items.Length; | |
[MethodImpl(MethodImplOptions.NoInlining)] | |
int Execute(ReadOnlySpan<int> items) => items.Length; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment