HexMate
HexMate copied to clipboard
ToHex / FromHex in Ethereum
We use a lot of FromHex / ToHex and we have two switches that we need to use:
- Include 0x that pushes 0x characters in front
- remove leading zeros (thyat removes all leading zeros at the end
the second is problematic because it may lead to a change of length and oddity
I am running benchmarks of your method now against our fastest implementation
private struct StateSmall
{
public StateSmall(byte[] bytes, bool withZeroX)
{
Bytes = bytes;
WithZeroX = withZeroX;
}
public byte[] Bytes;
public bool WithZeroX;
}
private static uint[] Lookup32 = CreateLookup32("x2");
private static uint[] CreateLookup32(string format)
{
uint[] result = new uint[256];
for (int i = 0; i < 256; i++)
{
string s = i.ToString(format);
result[i] = s[0] + ((uint) s[1] << 16);
}
return result;
}
[DebuggerStepThrough]
public static string ByteArrayToHexViaLookup32Safe(byte[] bytes, bool withZeroX)
{
if (bytes.Length == 0)
{
return withZeroX ? "0x" : "";
}
int length = bytes.Length * 2 + (withZeroX ? 2 : 0);
StateSmall stateToPass = new StateSmall(bytes, withZeroX);
return string.Create(length, stateToPass, (chars, state) =>
{
int offset0x = 0;
if (state.WithZeroX)
{
chars[0] = '0';
chars[1] = 'x';
offset0x += 2;
}
Span<uint> charsAsInts = MemoryMarshal.Cast<char, uint>(chars.Slice(offset0x));
int targetLength = state.Bytes.Length;
for (int i = 0; i < targetLength; i += 1)
{
uint val = Lookup32[state.Bytes[i]];
charsAsInts[i] = val;
}
});
}
[MemoryDiagnoser]
[CoreJob(baseline: true)]
public class ByteArrayToHexBenchmarks
{
private byte[] array = Bytes.FromHexString("0123456789abcdef");
[GlobalSetup]
public void Setup()
{
}
[Benchmark(Baseline = true)]
public string Current()
{
return array.ToHexString(true);
}
[Benchmark]
public string Improved()
{
return Bytes.ByteArrayToHexViaLookup32Safe(array, true);
}
[Benchmark]
public string HexMateA()
{
return Convert.ToHexString(array, HexFormattingOptions.Lowercase);
}
}
Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|
Current | 53.14 ns | 0.8076 ns | 0.6744 ns | 1.00 | 0.00 | 0.0136 | - | - | 64 B |
Improved | 18.87 ns | 0.4436 ns | 0.6072 ns | 0.35 | 0.01 | 0.0136 | - | - | 64 B |
HexMateA | 35.21 ns | 0.7732 ns | 1.2704 ns | 0.66 | 0.02 | 0.0119 | - | - | 56 B |
I need to run it with big inputs (up to L2)
Yup, HexMate is faster for anything longer, interestingly we call it a lot but 99% of times have arrays of 32 bytes there.