Testing Abstraction techniques...
Source Code [click to expand]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using static Benchmarks.Syntax;
namespace Benchmarks
{
public class Benchmarks
{
public static int sum;
public static int[] array;
[IterationSetup] public void Setup()
{
const int size = 100;
sum = 0;
array = new int[size];
for (int i = 0; i < size; i++)
{
array[i] = i;
}
}
[Benchmark] public void Control()
{
sum = 0;
foreach (int i in array)
{
sum += i;
}
}
[Benchmark]
public void Stepper1() =>
array.Stepper1<int, StepInt>();
[Benchmark]
public void Stepper2() =>
array.Stepper2<int, StepInt>();
[Benchmark]
public void StepperBreak1() =>
array.StepperBreak1<int, StepBreakInt>();
[Benchmark]
public void StepperBreak2() =>
array.StepperBreak2<int, StepBreakInt>();
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
[Benchmark]
public void Stepper1AgrOp() =>
array.Stepper1AgrOp<int, StepIntAgrOp>();
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
[Benchmark]
public void Stepper2AgrOp() =>
array.Stepper2AgrOp<int, StepIntAgrOp>();
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
[Benchmark]
public void StepperBreak1AgrOp() =>
array.StepperBreak1AgrOp<int, StepBreakIntAgrOp>();
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
[Benchmark]
public void StepperBreak2AgrOp() =>
array.StepperBreak2AgrOp<int, StepBreakIntAgrOp>();
}
public struct StepInt : IStep<int>
{
public void Do(int a) => Benchmarks.sum += a;
}
public struct StepBreakInt : IStepBreak<int>
{
public StepStatus Do(int a) { Benchmarks.sum += a; return Continue; }
}
public struct StepIntAgrOp : IStep<int>
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void Do(int a) =>
Benchmarks.sum += a;
}
public struct StepBreakIntAgrOp : IStepBreak<int>
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public StepStatus Do(int a)
{
Benchmarks.sum += a;
return Continue;
}
}
public struct StepToStepBreak<T, Step> : IStepBreak<T>
where Step : struct, IStep<T>
{
public Step StepFunction;
public StepToStepBreak(Step step) { StepFunction = step; }
public StepStatus Do(T a) { StepFunction.Do(a); return Continue; }
}
public struct StepToStepBreakAgrOp<T, Step> : IStepBreak<T>
where Step : struct, IStep<T>
{
public Step StepFunction;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public StepToStepBreakAgrOp(Step step) { StepFunction = step; }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public StepStatus Do(T a) { StepFunction.Do(a); return Continue; }
}
public interface IAction<A> { void Do(A a); }
public interface IFunction<A, B> { B Do(A a); }
public interface IStep<A> : IAction<A> { }
public interface IStepBreak<A> : IFunction<A, StepStatus> { }
public enum StepStatus
{
Continue = 0,
Break = 1,
};
public static class Syntax
{
public const StepStatus Break = StepStatus.Break;
public const StepStatus Continue = StepStatus.Continue;
}
public static class Extensions
{
public static void Stepper1<T, Step>(this T[] array)
where Step : struct, IStep<T>
{
Step step = default;
foreach (T value in array)
{
step.Do(value);
}
}
public static void Stepper2<T, Step>(this T[] array)
where Step : struct, IStep<T> =>
StepperBreak1<T, StepToStepBreak<T, Step>>(array);
public static StepStatus StepperBreak1<T, Step>(this T[] array)
where Step : struct, IStepBreak<T>
{
Step step = default;
foreach (T value in array)
{
if (step.Do(value) is Break)
{
return Break;
}
}
return Continue;
}
public static StepStatus StepperBreak2<T, Step>(this T[] array)
where Step : struct, IStepBreak<T>
{
Step step = default;
foreach (T value in array)
{
step.Do(value);
if (Continue is Break)
{
return Break;
}
}
return Continue;
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static void Stepper1AgrOp<T, Step>(this T[] array)
where Step : struct, IStep<T>
{
Step step = default;
foreach (T value in array)
{
step.Do(value);
}
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static void Stepper2AgrOp<T, Step>(this T[] array)
where Step : struct, IStep<T> =>
StepperBreak1AgrOp<T, StepToStepBreakAgrOp<T, Step>>(array);
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static StepStatus StepperBreak1AgrOp<T, Step>(this T[] array)
where Step : struct, IStepBreak<T>
{
Step step = default;
foreach (T value in array)
{
if (step.Do(value) is Break)
{
return Break;
}
}
return Continue;
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static StepStatus StepperBreak2AgrOp<T, Step>(this T[] array)
where Step : struct, IStepBreak<T>
{
Step step = default;
foreach (T value in array)
{
step.Do(value);
if (Continue is Break)
{
return Break;
}
}
return Continue;
}
}
public class Program
{
public static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<Benchmarks>();
}
}
}
Benchmark Results [click to expand]
BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362
Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.0.100
[Host] : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT
Job-HLQRCV : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT
InvocationCount=1 UnrollFactor=1
Method | Mean | Error | StdDev | Median |
---|---|---|---|---|
Control | 187.0 ns | 12.01 ns | 33.86 ns | 200.0 ns |
Steppper1 | 289.6 ns | 10.64 ns | 30.71 ns | 300.0 ns |
Steppper2 | 250.0 ns | 0.00 ns | 0.00 ns | 250.0 ns |
StepperBreak1 | 300.0 ns | 0.00 ns | 0.00 ns | 300.0 ns |
StepperBreak2 | 189.2 ns | 13.10 ns | 34.96 ns | 200.0 ns |
Steppper1AgrOp | 271.9 ns | 16.06 ns | 46.35 ns | 250.0 ns |
Steppper2AgrOp | 186.3 ns | 12.04 ns | 34.55 ns | 200.0 ns |
StepperBreak1AgrOp | 275.0 ns | 16.96 ns | 50.00 ns | 300.0 ns |
StepperBreak2AgrOp | 290.7 ns | 10.74 ns | 29.22 ns | 300.0 ns |