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!