Skip to content

Instantly share code, notes, and snippets.

@mbcrawfo
Created August 10, 2022 19:52
Show Gist options
  • Save mbcrawfo/1fae97185df320e7b48a8c56eb82a1fe to your computer and use it in GitHub Desktop.
Save mbcrawfo/1fae97185df320e7b48a8c56eb82a1fe to your computer and use it in GitHub Desktop.
MulAdd Benchmarks
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkRunner.Run<Benchmark>();
public class Benchmark
{
private readonly Random _random = new(314159265);
[Benchmark]
public uint ManualInlining()
{
var c0 = 0u;
var c1 = 0u;
var c2 = 0u;
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
return c2 + c0;
}
[Benchmark]
public uint ManualInliningWithFastBool()
{
var c0 = 0u;
var c1 = 0u;
var c2 = 0u;
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
{
var t = (ulong)((uint)_random.Next() * (uint)_random.Next());
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
return c2 + c0;
}
[Benchmark]
public uint NormalMethod()
{
var c0 = 0u;
var c1 = 0u;
var c2 = 0u;
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
return c2 + c0;
}
[Benchmark]
public uint AggressiveInlining()
{
var c0 = 0u;
var c1 = 0u;
var c2 = 0u;
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_Inlined((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
return c2 + c0;
}
[Benchmark]
public uint AggressiveInliningWithFastBool()
{
var c0 = 0u;
var c1 = 0u;
var c2 = 0u;
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
MulAdd_InlinedWithFastBool((uint)_random.Next(), (uint)_random.Next(), ref c0, ref c1, ref c2);
return c2 + c0;
}
private static void MulAdd(uint a, uint b, ref uint c0, ref uint c1, ref uint c2)
{
var t = (ulong)a * b;
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void MulAdd_Inlined(uint a, uint b, ref uint c0, ref uint c1, ref uint c2)
{
var t = (ulong)a * b;
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += c0 < tl ? 1U : 0;
c1 += th;
c2 += c1 < th ? 1U : 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void MulAdd_InlinedWithFastBool(uint a, uint b, ref uint c0, ref uint c1, ref uint c2)
{
var t = (ulong)a * b;
var th = (uint)(t >> 32);
var tl = (uint)t;
c0 += tl;
th += BoolToUnsigned(c0 < tl);
c1 += th;
c2 += BoolToUnsigned(c1 < th);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint BoolToUnsigned(bool b) => Unsafe.As<bool, uint>(ref b);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment