Skip to content

Instantly share code, notes, and snippets.

@ZacharyPatten
Last active November 12, 2019 01:15
Show Gist options
  • Save ZacharyPatten/307bbf6a9ef80d5181d8000919802eed to your computer and use it in GitHub Desktop.
Save ZacharyPatten/307bbf6a9ef80d5181d8000919802eed to your computer and use it in GitHub Desktop.
Benchmarking Abstraction Techniques

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment