Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimized HexToBytes function #469

Merged
merged 1 commit into from
Oct 30, 2024

Conversation

irodai-majom
Copy link
Contributor

@irodai-majom irodai-majom commented Oct 24, 2024

  • used Span
  • used Convert.FromHexString vectorized function

Used following bechmark to check the speedup:

  • HexToBytesSubstring is existing implementation
  • HexToBytesSlice uses Span
  • HexToBytesConvert uses Span and Convert.FromHexString library function

Results are:

| Method              | N    | Mean         | Error      | StdDev     | Median       | Allocated |
|-------------------- |----- |-------------:|-----------:|-----------:|-------------:|----------:|
| HexToBytesSubstring | 100  |  1,132.54 ns |  22.490 ns |  47.440 ns |  1,110.33 ns |    3328 B |
| HexToBytesSlice     | 100  |    583.82 ns |  11.586 ns |  23.668 ns |    570.63 ns |     128 B |
| HexToBytesConvert   | 100  |     35.21 ns |   0.570 ns |   0.818 ns |     34.87 ns |     128 B |
| HexToBytesSubstring | 200  |  2,223.98 ns |  10.471 ns |   9.282 ns |  2,224.93 ns |    6624 B |
| HexToBytesSlice     | 200  |  1,154.18 ns |   5.493 ns |   4.587 ns |  1,152.19 ns |     224 B |
| HexToBytesConvert   | 200  |     61.24 ns |   0.875 ns |   0.731 ns |     60.86 ns |     224 B |
| HexToBytesSubstring | 1000 | 12,626.88 ns | 220.443 ns | 195.417 ns | 12,550.49 ns |   33024 B |
| HexToBytesSlice     | 1000 |  5,764.04 ns |  10.413 ns |   8.696 ns |  5,764.74 ns |    1024 B |
| HexToBytesConvert   | 1000 |    296.51 ns |   0.337 ns |   0.315 ns |    296.47 ns |    1024 B |

You can notice improvement in both execution time and memory allocated.

Benchmark code:

using System.Globalization;
using System.Text;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace N_m3u8DL-RE.Benchmark;

static class HexStingUtil
{
    public static byte[] HexToBytesSubstring(string hex)
    {
        hex = hex.Trim();
        if (hex.StartsWith("0x") || hex.StartsWith("0X"))
            hex = hex.Substring(2);
        byte[] bytes = new byte[hex.Length / 2];

        for (int i = 0; i < hex.Length; i += 2)
        {
            var hexDigit = hex.Substring(i, 2);
            bytes[i / 2] = Convert.ToByte(hexDigit, 16);
        }

        return bytes;
    }

    public static byte[] HexToBytesSlice(string hex)
    {
        var hexSpan = hex.AsSpan().Trim();
        if (hexSpan.StartsWith("0x") || hexSpan.StartsWith("0X"))
        {
            hexSpan = hexSpan.Slice(2);
        }
        
        byte[] bytes = new byte[hex.Length / 2];
        
        for (int i = 0; i < hex.Length; i += 2)
        {
            ReadOnlySpan<char> hexDigit = hexSpan.Slice(start: i, length: 2);
            var val = byte.Parse(hexDigit, NumberStyles.HexNumber);
            bytes[i / 2] = val;
        }

        return bytes;
    }

    public static byte[] HexToBytesConvert(string hex)
    {
        var hexSpan = hex.AsSpan().Trim();
        if (hexSpan.StartsWith("0x") || hexSpan.StartsWith("0X"))
        {
            hexSpan = hexSpan.Slice(2);
        }

        return Convert.FromHexString(hexSpan);
    }
}

[MemoryDiagnoser(false)]
public class HexStringToBytesBenchmark
{
    [Params(100, 200, 1000)] public int N;
    private string hexString;

    [GlobalSetup]
    public void Setup()
    {
        var buffer = new byte[N];
        new Random().NextBytes(buffer);

        hexString = Convert.ToHexString(buffer);
    }

    [Benchmark]
    public byte[] HexToBytesSubstring() => HexStingUtil.HexToBytesSubstring(hexString);

    [Benchmark]
    public byte[] HexToBytesSlice() => HexStingUtil.HexToBytesSlice(hexString);

    [Benchmark]
    public byte[] HexToBytesConvert() => HexStingUtil.HexToBytesConvert(hexString);
}

public class RunBenchmark
{
    public static void Main(string[] args)
    {
        var summary = BenchmarkRunner.Run<HexStringToBytesBenchmark>();
    }
}

- used Span
- used Convert.FromHexString vectorized function
@nilaoda
Copy link
Owner

nilaoda commented Oct 30, 2024

Thx.

@nilaoda nilaoda merged commit 9c49fce into nilaoda:main Oct 30, 2024
@irodai-majom irodai-majom deleted the optimize-hex-to-bytes branch November 10, 2024 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants