Skip to content

Instantly share code, notes, and snippets.

@mgravell
Last active June 16, 2020 21:47
Show Gist options
  • Save mgravell/0f728b919d40d718b504c5144a9833f9 to your computer and use it in GitHub Desktop.
Save mgravell/0f728b919d40d718b504c5144a9833f9 to your computer and use it in GitHub Desktop.
Compare Span vs params array performance
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