Skip to content

Instantly share code, notes, and snippets.

@Sergio0694
Created December 3, 2019 10:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Sergio0694/1f48c898d95a1de1bbee69fba1d5c022 to your computer and use it in GitHub Desktop.
Save Sergio0694/1f48c898d95a1de1bbee69fba1d5c022 to your computer and use it in GitHub Desktop.
A benchmark of various no-branch methods to compute the max of two int values
using System;
using System.Buffers;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
namespace MaxBenchmark
{
class Program
{
static void Main()
{
BenchmarkRunner.Run<MaxBenchmark>();
}
}
public class MaxBenchmark
{
[Params(256, 10_000, 1_000_000)]
public int N;
public int[] A;
public int[] B;
public int[] C;
[GlobalSetup]
public void Setup()
{
A = ArrayPool<int>.Shared.Rent(N);
B = ArrayPool<int>.Shared.Rent(N);
C = ArrayPool<int>.Shared.Rent(N);
var random = new Random(42);
for (int i = 0; i < N; i++)
{
A[i] = random.Next(-N, N + 1);
B[i] = random.Next(-N, N + 1);
}
}
[GlobalCleanup]
public void Cleanup()
{
ArrayPool<int>.Shared.Return(A);
ArrayPool<int>.Shared.Return(B);
ArrayPool<int>.Shared.Return(C);
}
[Benchmark]
public void Branch()
{
int n = N;
ref int
ra = ref A[0],
rb = ref B[0],
rc = ref C[0];
for (int i = 0; i < n; i++)
Unsafe.Add(ref rc, i) = MathI.Max0(Unsafe.Add(ref ra, i), Unsafe.Add(ref rb, i));
}
[Benchmark]
public void NoBranch()
{
int n = N;
ref int
ra = ref A[0],
rb = ref B[0],
rc = ref C[0];
for (int i = 0; i < n; i++)
Unsafe.Add(ref rc, i) = MathI.Max1(Unsafe.Add(ref ra, i), Unsafe.Add(ref rb, i));
}
[Benchmark]
public void NoBranch64bit()
{
int n = N;
ref int
ra = ref A[0],
rb = ref B[0],
rc = ref C[0];
for (int i = 0; i < n; i++)
Unsafe.Add(ref rc, i) = MathI.Max2(Unsafe.Add(ref ra, i), Unsafe.Add(ref rb, i));
}
[Benchmark]
public void NoBranch32bitNoOverflow()
{
int n = N;
ref int
ra = ref A[0],
rb = ref B[0],
rc = ref C[0];
for (int i = 0; i < n; i++)
Unsafe.Add(ref rc, i) = MathI.Max3(Unsafe.Add(ref ra, i), Unsafe.Add(ref rb, i));
}
}
public static class MathI
{
/// <summary>
/// Computes the maximum value between two values with no branch instructions
/// </summary>
/// <param name="a">The first input value</param>
/// <param name="b">The second input value</param>
/// <returns>The maximum value between <paramref name="a"/> and <paramref name="b"/></returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Max0(int a, int b) => a > b ? a : b;
/// <summary>
/// Computes the maximum value between two values with no branch instructions
/// </summary>
/// <param name="a">The first input value</param>
/// <param name="b">The second input value</param>
/// <returns>The maximum value between <paramref name="a"/> and <paramref name="b"/></returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Max1(int a, int b)
{
int sign = ((b - a) >> 31) & 1;
return a * sign + b * (sign ^ 1);
}
/// <summary>
/// Computes the maximum value between two values with no branch instructions
/// </summary>
/// <param name="a">The first input value</param>
/// <param name="b">The second input value</param>
/// <returns>The maximum value between <paramref name="a"/> and <paramref name="b"/></returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Max2(int a, int b)
{
long la = a, lb = b;
int sign = (int)(((lb - la) >> 63) & 1);
return a * sign + b * (sign ^ 1);
}
/// <summary>
/// Computes the maximum value between two values with no branch instructions
/// </summary>
/// <param name="a">The first input value</param>
/// <param name="b">The second input value</param>
/// <returns>The maximum value between <paramref name="a"/> and <paramref name="b"/></returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Max3(int a, int b)
{
int
c = a - b,
sa = 1 ^ ((a >> 31) & 1),
sb = 1 ^ ((b >> 31) & 1),
sc = 1 ^ ((c >> 31) & 1),
use_sa = sa ^ sb,
use_sc = 1 ^ (sa ^ sb),
k = use_sa * sa + use_sc * sc,
q = 1 ^ k;
return a * k + b * q;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment