A Pointless Performance Optimisation
Taking a simple, fast function and changing it into a complex, faster function.
I needed a function to output a char '0' when called with true, and '1' when called with false. Pretty straightforward:
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static char ToBit(this bool value) => value ? '1' : '0';Can we make it faster though? I don't really need it to be any faster of course, but that's no reason not to try. The function branches due to the ternary operator - if we could make it branchless then it will probably be faster. Luckily 0 and 1 come after each other in the ASCII table so if we could convert our value to an integer with 0 for false and 1 for true then we could simply add it to '0'. How can we do that though? Unsafe to the rescue!
A C# bool is represented under the hood as a byte, with 1 for true and 0 for false. Whilst this isn't specified by the C# spec, (I believe) Roslyn does enforce this behaviour, and indeed it's relied on for some JIT optimisations. Armed with that knowledge we can use Unsafe.As to convert the bool into a byte and simply add it to '0':
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static char ToBit(this bool value)
{
var @byte = Unsafe.As<bool, byte>(ref value);
return (char)(@byte + '0');
}How much faster is it though? Here are the results for a benchmark of converting 10 million random bool values:
| Method | Mean | Error | StdDev | Ratio |
|------------------ |----------:|----------:|----------:|------:|
| Ternary | 34.268 ms | 0.2554 ms | 0.2389 ms | 1.00 |
| Unsafe | 4.068 ms | 0.0539 ms | 0.0450 ms | 0.12 |Not bad!