Open
Description
Random.Shared.GetHexString
can be optimized by:
- Getting 2 (instead of 1) hex chars from every random byte.
- Taking advantage of the magic in HexConverter.
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkRunner.Run<Benchmarks>();
public class Benchmarks
{
[Params(2, 5, 8, 16, 32, 64)]
public int HexStringLength;
[Benchmark]
public char Main()
{
Span<char> buffer = stackalloc char[HexStringLength];
Random.Shared.GetHexString(buffer);
return buffer[0];
}
[Benchmark]
public char Opt()
{
Span<char> buffer = stackalloc char[HexStringLength];
Random_GetHexString(buffer);
return buffer[0];
}
private static void Random_GetHexString(Span<char> destination, bool lowercase = false)
{
// Scale the string length up to the nearest multiple of 16.
// For production code, we should slice destionation into small pieces first in case it's large.
Span<byte> source = stackalloc byte[((destination.Length + 15) & -16) / 2];
Random.Shared.NextBytes(source);
if (destination.Length % 2 == 1)
{
destination[^1] = GetHexChoices(lowercase)[source[^1] & 15];
}
source = source[..(destination.Length / 2)];
_ = lowercase ? Convert.TryToHexStringLower(source, destination, out _)
: Convert.TryToHexString(source, destination, out _);
}
private static ReadOnlySpan<char> GetHexChoices(bool lowercase) =>
lowercase ? "0123456789abcdef" : "0123456789ABCDEF";
}
Method | HexStringLength | Mean | Error | StdDev |
---|---|---|---|---|
Main | 2 | 8.284 ns | 0.0285 ns | 0.0252 ns |
Opt | 2 | 7.438 ns | 0.0108 ns | 0.0101 ns |
Main | 5 | 10.838 ns | 0.0071 ns | 0.0066 ns |
Opt | 5 | 9.590 ns | 0.0476 ns | 0.0397 ns |
Main | 8 | 10.979 ns | 0.0118 ns | 0.0099 ns |
Opt | 8 | 7.262 ns | 0.0813 ns | 0.0721 ns |
Main | 16 | 17.104 ns | 0.0292 ns | 0.0228 ns |
Opt | 16 | 8.030 ns | 0.0201 ns | 0.0188 ns |
Main | 32 | 26.957 ns | 0.1203 ns | 0.0939 ns |
Opt | 32 | 11.416 ns | 0.0169 ns | 0.0141 ns |
Main | 64 | 57.944 ns | 0.3560 ns | 0.3330 ns |
Opt | 64 | 17.249 ns | 0.0897 ns | 0.0839 ns |