From bb1a81e0e2f2da5b09d5b6d614f5b6f3715a17f8 Mon Sep 17 00:00:00 2001 From: benaadams Date: Thu, 2 Mar 2023 15:53:35 +0000 Subject: [PATCH 1/3] Faster IsZero for bytes --- .../Nethermind.Core/Extensions/Bytes.cs | 24 +++---------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs index b1bab877fbf..ee3ac273a52 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs @@ -151,35 +151,17 @@ public static bool AreEqual(Span a1, Span a2) public static bool IsZero(this byte[] bytes) { - return IsZero((ReadOnlySpan)bytes); + return bytes.AsSpan().IndexOfAnyExcept((byte)0) < 0; } public static bool IsZero(this Span bytes) { - return IsZero((ReadOnlySpan)bytes); + return bytes.IndexOfAnyExcept((byte)0) < 0; } public static bool IsZero(this ReadOnlySpan bytes) { - if (bytes.Length == 32) - { - return bytes[31] == 0 && bytes.SequenceEqual(Zero32); - } - - for (int i = 0; i < bytes.Length / 2; i++) - { - if (bytes[i] != 0) - { - return false; - } - - if (bytes[bytes.Length - i - 1] != 0) - { - return false; - } - } - - return bytes.Length % 2 == 0 || bytes[bytes.Length / 2] == 0; + return bytes.IndexOfAnyExcept((byte)0) < 0; } public static int LeadingZerosCount(this Span bytes, int startIndex = 0) From a079c42c2f50a9590ec61caf3200490969c4bbe9 Mon Sep 17 00:00:00 2001 From: benaadams Date: Thu, 2 Mar 2023 18:23:56 +0000 Subject: [PATCH 2/3] Vectorize other ops --- .../Nethermind.Core.Test/BytesTests.cs | 7 ++++ .../Json/ByteArray32ConverterTests.cs | 14 +++++++- .../Nethermind.Core.Test/KeccakTests.cs | 2 ++ .../Nethermind.Core/Extensions/Bytes.cs | 33 ++++--------------- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/Nethermind/Nethermind.Core.Test/BytesTests.cs b/src/Nethermind/Nethermind.Core.Test/BytesTests.cs index fffbf5d7ee8..116a1ad1b57 100644 --- a/src/Nethermind/Nethermind.Core.Test/BytesTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/BytesTests.cs @@ -275,6 +275,13 @@ public void Get_bit_works(byte value, int position, bool expectedResult) [TestCase("0x", 0)] [TestCase("0x1000", 1)] + [TestCase("0x100000", 2)] + [TestCase("0x10000000", 3)] + [TestCase("0x1000000000", 4)] + [TestCase("0x100000000000", 5)] + [TestCase("0x10000000000000", 6)] + [TestCase("0x1000000000000000", 7)] + [TestCase("0x100000000000000000", 8)] [TestCase("0x0000", 2)] [TestCase("0x000100", 1)] public void Trailing_zeros_count_works(string hex, int expectedResult) diff --git a/src/Nethermind/Nethermind.Core.Test/Json/ByteArray32ConverterTests.cs b/src/Nethermind/Nethermind.Core.Test/Json/ByteArray32ConverterTests.cs index 64614e6cd29..8a5599a1d65 100644 --- a/src/Nethermind/Nethermind.Core.Test/Json/ByteArray32ConverterTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/Json/ByteArray32ConverterTests.cs @@ -12,7 +12,6 @@ public class Bytes32ConverterTests : ConverterTestBase { [TestCase(null)] [TestCase(new byte[0])] - [TestCase(new byte[] { 1 })] public void Empty_value(byte[] values) { TestConverter( @@ -20,5 +19,18 @@ public void Empty_value(byte[] values) (before, after) => Bytes.AreEqual(before.WithoutLeadingZeros(), after.WithoutLeadingZeros()), new Bytes32Converter()); } + + [TestCase(new byte[] { 1 })] + [TestCase(new byte[] { 0, 1 })] + [TestCase(new byte[] { 0, 0, 1 })] + [TestCase(new byte[] { 0, 0, 1, 0 })] + [TestCase(new byte[] { 0, 0, 1, 0, 0 })] + public void NonEmpty_value(byte[] values) + { + TestConverter( + values, + (before, after) => Bytes.AreEqual(before.WithoutLeadingZeros(), after.WithoutLeadingZeros()), + new Bytes32Converter()); + } } } diff --git a/src/Nethermind/Nethermind.Core.Test/KeccakTests.cs b/src/Nethermind/Nethermind.Core.Test/KeccakTests.cs index cc4fd506c89..e73e70e07bd 100644 --- a/src/Nethermind/Nethermind.Core.Test/KeccakTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/KeccakTests.cs @@ -79,6 +79,8 @@ public void Zero() [TestCase("0x0000000000000000000000000000000000000000000000000000000000000000", null, -1)] [TestCase("0x0000000000000000000000000000000000000000000000000000000000000000", "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", -1)] + [TestCase("0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", 0)] + [TestCase("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", 0)] [TestCase("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "0x0000000000000000000000000000000000000000000000000000000000000000", 1)] public void Compare(string a, string b, int result) { diff --git a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs index ee3ac273a52..73cca9ca942 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs @@ -166,28 +166,14 @@ public static bool IsZero(this ReadOnlySpan bytes) public static int LeadingZerosCount(this Span bytes, int startIndex = 0) { - for (int i = startIndex; i < bytes.Length; i++) - { - if (bytes[i] != 0) - { - return i - startIndex; - } - } - - return bytes.Length - startIndex; + int nonZeroIndex = bytes[startIndex..].IndexOfAnyExcept((byte)0); + return nonZeroIndex < 0 ? bytes.Length - startIndex : nonZeroIndex; } public static int TrailingZerosCount(this byte[] bytes) { - for (int i = 0; i < bytes.Length; i++) - { - if (bytes[bytes.Length - i - 1] != 0) - { - return i; - } - } - - return bytes.Length; + int lastIndex = bytes.AsSpan().LastIndexOfAnyExcept((byte)0); + return lastIndex < 0 ? bytes.Length : bytes.Length - lastIndex - 1; } public static Span WithoutLeadingZeros(this byte[] bytes) @@ -197,15 +183,10 @@ public static Span WithoutLeadingZeros(this byte[] bytes) public static Span WithoutLeadingZeros(this Span bytes) { - for (int i = 0; i < bytes.Length; i++) - { - if (bytes[i] != 0) - { - return bytes.Slice(i, bytes.Length - i); - } - } + if (bytes.Length == 0) return new byte[] { 0 }; - return new byte[] { 0 }; + int nonZeroIndex = bytes.IndexOfAnyExcept((byte)0); + return nonZeroIndex < 0 ? bytes[^1..] : bytes[nonZeroIndex..]; } public static byte[] Concat(byte prefix, byte[] bytes) From 7a1e5487a9c027c374a0e090edbfd0ad15de6dce Mon Sep 17 00:00:00 2001 From: benaadams Date: Fri, 3 Mar 2023 10:54:19 +0000 Subject: [PATCH 3/3] Combine tests --- .../Json/ByteArray32ConverterTests.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/Nethermind/Nethermind.Core.Test/Json/ByteArray32ConverterTests.cs b/src/Nethermind/Nethermind.Core.Test/Json/ByteArray32ConverterTests.cs index 8a5599a1d65..966143219fe 100644 --- a/src/Nethermind/Nethermind.Core.Test/Json/ByteArray32ConverterTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/Json/ByteArray32ConverterTests.cs @@ -12,20 +12,12 @@ public class Bytes32ConverterTests : ConverterTestBase { [TestCase(null)] [TestCase(new byte[0])] - public void Empty_value(byte[] values) - { - TestConverter( - values, - (before, after) => Bytes.AreEqual(before.WithoutLeadingZeros(), after.WithoutLeadingZeros()), - new Bytes32Converter()); - } - [TestCase(new byte[] { 1 })] [TestCase(new byte[] { 0, 1 })] [TestCase(new byte[] { 0, 0, 1 })] [TestCase(new byte[] { 0, 0, 1, 0 })] [TestCase(new byte[] { 0, 0, 1, 0, 0 })] - public void NonEmpty_value(byte[] values) + public void ValueWithAndWithoutLeadingZeros_are_equal(byte[] values) { TestConverter( values,