Skip to content

Commit

Permalink
Add 256bit and 512bit vectorized hex decode (#7226)
Browse files Browse the repository at this point in the history
  • Loading branch information
benaadams authored Jun 27, 2024
1 parent 24f4142 commit 82b84b2
Show file tree
Hide file tree
Showing 6 changed files with 289 additions and 41 deletions.
3 changes: 2 additions & 1 deletion src/Nethermind/Nethermind.Benchmark.Runner/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using System.Linq;
using BenchmarkDotNet.Toolchains.InProcess.NoEmit;

namespace Nethermind.Benchmark.Runner
{
Expand All @@ -19,7 +20,7 @@ public DashboardConfig(params Job[] jobs)
{
foreach (Job job in jobs)
{
AddJob(job);
AddJob(job.WithToolchain(InProcessNoEmitToolchain.Instance));
}

AddColumnProvider(BenchmarkDotNet.Columns.DefaultColumnProviders.Descriptor);
Expand Down
44 changes: 28 additions & 16 deletions src/Nethermind/Nethermind.Benchmark/Core/FromHexBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,48 @@ namespace Nethermind.Benchmarks.Core
{
public class FromHexBenchmarks
{
private string hex = "0123456789abcdef";
private byte[] _hex;
private byte[] _bytes;

[Params(true, false)]
public bool With0xPrefix;

[Params(true, false)]
public bool OddNumber;
[Params(32, 64, 128, 256, 512, 1024)]
public int ByteLength;

[GlobalSetup]
public void Setup()
{
//Test Performance of odd number
if (OddNumber)
hex = "5" + hex;
_hex = new byte[ByteLength * 2];
_bytes = new byte[ByteLength];

for (int i = 0; i < _bytes.Length; i++)
{
_bytes[i] = (byte)i;
}

//Test performance of hex
if (With0xPrefix)
hex = "0x" + hex;
Bytes.OutputBytesToByteHex(_bytes, _hex, extraNibble: false);
}

[Benchmark(Baseline = true)]
public byte[] Current()
public bool Scalar()
{
return HexConverter.TryDecodeFromUtf8_Scalar(_hex, _bytes, isOdd: false);
}

[Benchmark]
public bool Vector128()
{
return HexConverter.TryDecodeFromUtf8_Vector128(_hex, _bytes);
}

[Benchmark]
public bool Vector256()
{
return Bytes.FromHexString(hex);
return HexConverter.TryDecodeFromUtf8_Vector256(_hex, _bytes);
}

[Benchmark]
public byte[] Improved()
public bool Vector512()
{
return Bytes.FromHexString(hex);
return HexConverter.TryDecodeFromUtf8_Vector512(_hex, _bytes);
}
}
}
50 changes: 50 additions & 0 deletions src/Nethermind/Nethermind.Core.Test/BytesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
using System.Numerics;
using FluentAssertions;
using Nethermind.Core.Extensions;
using Nethermind.Serialization.Json;

using NUnit.Framework;

namespace Nethermind.Core.Test
Expand Down Expand Up @@ -324,6 +326,54 @@ public void Can_create_bit_array_from_bytes(string hex, string expectedResult)
Assert.That(Bytes.FromHexString(expectedResult), Is.EqualTo(input));
}

[TestCase]
public void Can_roundtrip_utf8_hex_conversion()
{
for (var i = 0; i < 2048; i++)
{
byte[] input = new byte[i];
byte[] hex = new byte[i * 2];
TestContext.CurrentContext.Random.NextBytes(input);

Bytes.OutputBytesToByteHex(input, hex, extraNibble: false);

byte[] output = Bytes.FromUtf8HexString(hex);

Assert.That(output, Is.EqualTo(input));
}
}

[TestCase(1)]
[TestCase(2)]
[TestCase(4)]
[TestCase(8)]
[TestCase(16)]
[TestCase(32)]
[TestCase(64)]
[TestCase(128)]
[TestCase(256)]
[TestCase(512)]
public void Invalid_utf8_hex_conversion_fails(int length)
{
byte[] input = new byte[length];
byte[] hex = new byte[length * 2];
TestContext.CurrentContext.Random.NextBytes(input);

Bytes.OutputBytesToByteHex(input, hex, extraNibble: false);

for (var i = 0; i < hex.Length; i++)
{
byte b = hex[i];

hex[i] = (byte)' ';
Assert.Throws<FormatException>(() => Bytes.FromUtf8HexString(hex));

hex[i] = b;
byte[] output = Bytes.FromUtf8HexString(hex);
Assert.That(output, Is.EqualTo(input));
}
}

public static IEnumerable OrTests
{
get
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System.IO;
using System.Text;
using FluentAssertions;
using Nethermind.Core.Extensions;
using Nethermind.Serialization.Json;
Expand All @@ -22,6 +20,22 @@ public void Test_roundtrip(byte[]? bytes)
TestConverter(bytes, (before, after) => Bytes.AreEqual(before, after), new ByteArrayConverter());
}

[Test]
public void Test_roundtrip_large()
{
ByteArrayConverter converter = new();
for (var i = 0; i < 1024; i++)
{
byte[] bytes = new byte[i];
for (var j = 0; j < i; j++)
{
bytes[j] = (byte)j;
}

TestConverter(bytes, (before, after) => Bytes.AreEqual(before, after), converter);
}
}

[Test]
public void Direct_null()
{
Expand Down
14 changes: 1 addition & 13 deletions src/Nethermind/Nethermind.Core/Extensions/Bytes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1033,19 +1033,7 @@ public static void FromUtf8HexString(ReadOnlySpan<byte> hexString, Span<byte> re
ThrowInvalidOperationException();
}

bool isSuccess;
if (oddMod == 0 &&
BitConverter.IsLittleEndian && (Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) &&
hexString.Length >= Vector128<byte>.Count)
{
isSuccess = HexConverter.TryDecodeFromUtf8_Vector128(hexString, result);
}
else
{
isSuccess = HexConverter.TryDecodeFromUtf8(hexString, result, oddMod == 1);
}

if (!isSuccess)
if (!HexConverter.TryDecodeFromUtf8(hexString, result))
{
ThrowFormatException_IncorrectHexString();
}
Expand Down
Loading

0 comments on commit 82b84b2

Please sign in to comment.