|
using System.Runtime.CompilerServices; |
|
using BenchmarkDotNet.Attributes; |
|
|
|
namespace ConsoleApp_Bench_1; |
|
|
|
/// <summary> |
|
/// Measures the effect of invoking an interface method (static or non-static) at |
|
/// the same call site with multiple types. |
|
/// </summary> |
|
/// <remarks> |
|
/// The goal here is not to measure the exact costs but to identify if there is a |
|
/// difference or not between the single type and the multiple types scenarios. |
|
/// </remarks> |
|
public class InterfaceDispatchBenchmarks_SingleTypeOrMultiTypes |
|
{ |
|
public static IAccessor[] Accessors1 { get; } = Enumerable.Range(0, OperationsPerInvoke).Select(x => (IAccessor)new Accessor1()).ToArray(); |
|
public static IAccessor[] Accessors2 { get; } = Enumerable.Range(0, OperationsPerInvoke).Select(x => (IAccessor)new Accessor2()).ToArray(); |
|
public static IAccessor[] Accessors3 { get; } = Enumerable.Range(0, OperationsPerInvoke).Select(x => (IAccessor)new Accessor3()).ToArray(); |
|
public static Accessor1[] Accessors1_Typed { get; } = Enumerable.Range(0, OperationsPerInvoke).Select(x => new Accessor1()).ToArray(); |
|
public static Accessor2[] Accessors2_Typed { get; } = Enumerable.Range(0, OperationsPerInvoke).Select(x => new Accessor2()).ToArray(); |
|
public static Accessor3[] Accessors3_Typed { get; } = Enumerable.Range(0, OperationsPerInvoke).Select(x => new Accessor3()).ToArray(); |
|
|
|
public const int OperationsPerInvoke = 100_000; |
|
|
|
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)] |
|
public int NonStatic_SingleType() |
|
=> NonStatic_Impl(Accessors1, Accessors1, Accessors1); |
|
|
|
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)] |
|
public int NonStatic_MultiTypes() |
|
=> NonStatic_Impl(Accessors1, Accessors2, Accessors3); |
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)] |
|
private static int NonStatic_Impl(IAccessor[] accessors1, IAccessor[] accessors2, IAccessor[] accessors3) |
|
{ |
|
var sum = 0; |
|
for (var i = 0; i < OperationsPerInvoke; i++) |
|
{ |
|
sum += Read(accessors1[i]); |
|
sum += Read(accessors2[i]); |
|
sum += Read(accessors3[i]); |
|
} |
|
|
|
return sum; |
|
|
|
// Forces a unique call site. |
|
[MethodImpl(MethodImplOptions.NoInlining)] |
|
static int Read(IAccessor accessor) |
|
=> accessor.Read(); |
|
} |
|
|
|
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)] |
|
public int Static_SingleType() |
|
=> Static_Impl(Accessors1_Typed, Accessors1_Typed, Accessors1_Typed); |
|
|
|
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)] |
|
public int Static_MultiTypes() |
|
=> Static_Impl(Accessors1_Typed, Accessors2_Typed, Accessors3_Typed); |
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)] |
|
private static int Static_Impl<T1, T2, T3>(T1[] accessors1, T2[] accessors2, T3[] accessors3) |
|
where T1 : IStaticAccessor<T1> |
|
where T2 : IStaticAccessor<T2> |
|
where T3 : IStaticAccessor<T3> |
|
{ |
|
var sum = 0; |
|
for (var i = 0; i < OperationsPerInvoke; i++) |
|
{ |
|
sum += Read(accessors1[i]); |
|
sum += Read(accessors2[i]); |
|
sum += Read(accessors3[i]); |
|
} |
|
|
|
return sum; |
|
|
|
// Forces a unique call site. |
|
[MethodImpl(MethodImplOptions.NoInlining)] |
|
static int Read<T>(T accessor) where T : IStaticAccessor<T> |
|
=> T.ReadStatic(accessor); |
|
} |
|
|
|
public interface IStaticAccessor<T> |
|
{ |
|
static abstract int ReadStatic(T target); |
|
} |
|
|
|
public interface IAccessor |
|
{ |
|
int Read(); |
|
} |
|
|
|
public class Accessor1 : IStaticAccessor<Accessor1>, IAccessor |
|
{ |
|
public int Value { get; set; } |
|
|
|
public static int ReadStatic(Accessor1 target) => target.Value; |
|
|
|
public int Read() => Value; |
|
} |
|
|
|
public class Accessor2 : IStaticAccessor<Accessor2>, IAccessor |
|
{ |
|
public int Value { get; set; } |
|
|
|
public static int ReadStatic(Accessor2 target) => target.Value; |
|
|
|
public int Read() => Value; |
|
} |
|
|
|
public class Accessor3 : IStaticAccessor<Accessor3>, IAccessor |
|
{ |
|
public int Value { get; set; } |
|
|
|
public static int ReadStatic(Accessor3 target) => target.Value; |
|
|
|
public int Read() => Value; |
|
} |
|
} |