Skip to content

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