Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
---|---|---|---|---|---|---|
A1 | 88.28 us | 1.739 us | 2.953 us | 31.1279 | 4.3945 | 399.68 KB |
A2 | 84.18 us | 1.644 us | 2.137 us | 30.5176 | 4.0283 | 390.72 KB |
A3 | 120.11 us | 0.894 us | 0.836 us | 5.9814 | 0.7324 | 78.22 KB |
B1 | 124.21 us | 0.311 us | 0.243 us | 2.9297 | - | 39.09 KB |
B2 | 122.88 us | 0.612 us | 0.543 us | 2.9297 | - | 39.09 KB |
B3 | 123.91 us | 0.480 us | 0.426 us | 2.9297 | - | 39.09 KB |
C1 | 109.96 us | 0.325 us | 0.272 us | 2.9297 | - | 39.09 KB |
A4 | 10.72 us | 0.208 us | 0.195 us | 6.8207 | 1.1292 | 87.18 KB |
B4 | 10.88 us | 0.126 us | 0.105 us | 3.0365 | - | 39.09 KB |
C2 | 10.31 us | 0.197 us | 0.193 us | 3.0365 | - | 39.09 KB |
Last active
December 11, 2024 12:48
-
-
Save ufcpp/bad4dd447cf061a73cace83238d60113 to your computer and use it in GitHub Desktop.
byte 列の16進ダンプ文字列化
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using BenchmarkDotNet.Attributes; | |
using System.Runtime.CompilerServices; | |
using System.Text; | |
using C = System.Globalization.CultureInfo; | |
#if DEBUG | |
var data = ToHexStringBenchmark.StaticData; | |
var s = data.A1(); | |
Console.WriteLine(data.A1() == s); | |
Console.WriteLine(data.A2() == s); | |
Console.WriteLine(data.A3() == s); | |
Console.WriteLine(data.B1() == s); | |
Console.WriteLine(data.B2() == s); | |
Console.WriteLine(data.B3() == s); | |
Console.WriteLine(data.C1() == s); | |
Console.WriteLine(data.A4() == s); | |
Console.WriteLine(data.B4() == s); | |
Console.WriteLine(data.C2() == s); | |
#else | |
BenchmarkDotNet.Running.BenchmarkRunner.Run<ToHexStringBenchmark>(); | |
#endif | |
public static class ToHexExtensions | |
{ | |
public static string A1(this ReadOnlySpan<byte> data) | |
{ | |
var x = new StringBuilder(); | |
foreach (var b in data) | |
{ | |
x.Append(b.ToString("X2", C.InvariantCulture)); | |
} | |
return x.ToString(); | |
} | |
public static string A2(this ReadOnlySpan<byte> data) | |
{ | |
var x = new StringBuilder(data.Length * 2); // capacity 追加 | |
foreach (var b in data) | |
{ | |
x.Append(b.ToString("X2", C.InvariantCulture)); | |
} | |
return x.ToString(); | |
} | |
public static string A3(this ReadOnlySpan<byte> data) | |
{ | |
var x = new StringBuilder(data.Length * 2); | |
foreach (var b in data) | |
{ | |
x.Append(C.InvariantCulture, $"{b:X2}"); // interpolation に | |
} | |
return x.ToString(); | |
} | |
public static string B1(this ReadOnlySpan<byte> data) | |
{ | |
var x = new DefaultInterpolatedStringHandler(0, 0, C.InvariantCulture); | |
foreach (var b in data) | |
{ | |
x.AppendFormatted(b, 0, "X2"); | |
} | |
return x.ToStringAndClear(); | |
} | |
public static string B2(this ReadOnlySpan<byte> data) | |
{ | |
var x = new DefaultInterpolatedStringHandler(data.Length * 2, 0, C.InvariantCulture); // literalLength に文字数追加 | |
foreach (var b in data) | |
{ | |
x.AppendFormatted(b, 0, "X2"); | |
} | |
return x.ToStringAndClear(); | |
} | |
[SkipLocalsInit] | |
public static string B3(this ReadOnlySpan<byte> data) | |
{ | |
var x = new DefaultInterpolatedStringHandler(0, 0, C.InvariantCulture, stackalloc char[data.Length * 2]); // initialBufer 追加 | |
foreach (var b in data) | |
{ | |
x.AppendFormatted(b, 0, "X2"); | |
} | |
return x.ToStringAndClear(); | |
} | |
// .NET 9 必須(allows ref struct が必須)。 | |
// でないと、SpanAction デリゲートに ReadOnlySpan を渡せない。 | |
public static string C1(this ReadOnlySpan<byte> data) | |
=> string.Create(data.Length * 2, data, static (buffer, data) => | |
{ | |
foreach (var b in data) | |
{ | |
b.TryFormat(buffer, out _, "X2", C.InvariantCulture); | |
buffer = buffer[2..]; | |
} | |
}); | |
private const string _hexChars = "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF"; | |
public static string A4(this ReadOnlySpan<byte> data) | |
{ | |
var x = new StringBuilder(); | |
foreach (var b in data) | |
{ | |
x.Append(_hexChars.AsSpan(2 * b, 2)); | |
} | |
return x.ToString(); | |
} | |
public static string B4(this ReadOnlySpan<byte> data) | |
{ | |
var x = new DefaultInterpolatedStringHandler(data.Length * 2, 0, C.InvariantCulture, stackalloc char[data.Length * 2]); // initialBufer 追加 | |
foreach (var b in data) | |
{ | |
x.AppendFormatted(_hexChars.AsSpan(2 * b, 2)); | |
} | |
return x.ToStringAndClear(); | |
} | |
// .NET 9 必須(allows ref struct が必須)。 | |
public static string C2(this ReadOnlySpan<byte> data) | |
=> string.Create(data.Length * 2, data, static (buffer, data) => | |
{ | |
foreach (var b in data) | |
{ | |
_hexChars.AsSpan(2 * b, 2).CopyTo(buffer); | |
buffer = buffer[2..]; | |
} | |
}); | |
} | |
[MemoryDiagnoser] | |
public class ToHexStringBenchmark | |
{ | |
public static ReadOnlySpan<byte> StaticData => [ | |
0,1,2,3,4,5,6,7,8,9, | |
10,11,12,13,14,15,16,17,18,19, | |
20,21,22,23,24,25,26,27,28,29, | |
30,31,32,33,34,35,36,37,38,39, | |
40,41,42,43,44,45,46,47,48,49, | |
50,51,52,53,54,55,56,57,58,59, | |
60,61,62,63,64,65,66,67,68,69, | |
70,71,72,73,74,75,76,77,78,79, | |
80,81,82,83,84,85,86,87,88,89, | |
90,91,92,93,94,95,96,97,98,99, | |
]; | |
private ReadOnlySpan<byte> Data => _data ??= Enumerable.Range(0, 10000).Select(x => (byte)x).ToArray(); | |
private byte[]? _data; | |
[GlobalSetup] | |
public void Setup() | |
{ | |
_ = Data; | |
} | |
[Benchmark] public string A1() => Data.A1(); | |
[Benchmark] public string A2() => Data.A2(); | |
[Benchmark] public string A3() => Data.A3(); | |
[Benchmark] public string B1() => Data.B1(); | |
[Benchmark] public string B2() => Data.B2(); | |
[Benchmark] public string B3() => Data.B3(); | |
[Benchmark] public string C1() => Data.C1(); | |
[Benchmark] public string A4() => Data.A4(); | |
[Benchmark] public string B4() => Data.B4(); | |
[Benchmark] public string C2() => Data.C2(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment