|
using System; |
|
using System.Runtime.CompilerServices; |
|
using System.Runtime.InteropServices; |
|
using System.Text; |
|
using BenchmarkDotNet.Attributes; |
|
using BenchmarkDotNet.Running; |
|
|
|
namespace Benchmark; |
|
|
|
public class Bench |
|
{ |
|
[Params(10, 100, 500, 1000, 10000, 1000000)] |
|
public int N; |
|
|
|
#region PrecomputedMappings |
|
|
|
private const string HexAlphabetString = "0123456789ABCDEF"; |
|
|
|
private static readonly char[] HexAlphabetArray = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; |
|
|
|
private static ReadOnlySpan<byte> HexAlphabetSpan => new[] |
|
{ |
|
(byte)'0', |
|
(byte)'1', |
|
(byte)'2', |
|
(byte)'3', |
|
(byte)'4', |
|
(byte)'5', |
|
(byte)'6', |
|
(byte)'7', |
|
(byte)'8', |
|
(byte)'9', |
|
(byte)'A', |
|
(byte)'B', |
|
(byte)'C', |
|
(byte)'D', |
|
(byte)'E', |
|
(byte)'F', |
|
}; |
|
|
|
// { "00", "01", ..., "0E", "0F", "10", "11", ..., "FE", "FF" } |
|
private static readonly string[] HexStringTable = |
|
HexAlphabetString.SelectMany(n1 => HexAlphabetString.Select(n2 => new string(new[] { n1, n2 }))).ToArray(); |
|
|
|
private static readonly uint[] Lookup32 = Enumerable.Range(0, 256).Select(i => |
|
{ |
|
string s = i.ToString("X2"); |
|
return s[0] + ((uint)s[1] << 16); |
|
}).ToArray(); |
|
|
|
private static readonly unsafe uint* Lookup32UnsafeP = (uint*)GCHandle.Alloc(Lookup32, GCHandleType.Pinned).AddrOfPinnedObject(); |
|
|
|
private static ReadOnlySpan<byte> Lookup32Span => new byte[] |
|
{ |
|
0x0, 0x30, 0x0, 0x30, |
|
0x0, 0x31, 0x0, 0x30, |
|
0x0, 0x32, 0x0, 0x30, |
|
0x0, 0x33, 0x0, 0x30, |
|
0x0, 0x34, 0x0, 0x30, |
|
0x0, 0x35, 0x0, 0x30, |
|
0x0, 0x36, 0x0, 0x30, |
|
0x0, 0x37, 0x0, 0x30, |
|
0x0, 0x38, 0x0, 0x30, |
|
0x0, 0x39, 0x0, 0x30, |
|
0x0, 0x61, 0x0, 0x30, |
|
0x0, 0x62, 0x0, 0x30, |
|
0x0, 0x63, 0x0, 0x30, |
|
0x0, 0x64, 0x0, 0x30, |
|
0x0, 0x65, 0x0, 0x30, |
|
0x0, 0x66, 0x0, 0x30, |
|
0x0, 0x30, 0x0, 0x31, |
|
0x0, 0x31, 0x0, 0x31, |
|
0x0, 0x32, 0x0, 0x31, |
|
0x0, 0x33, 0x0, 0x31, |
|
0x0, 0x34, 0x0, 0x31, |
|
0x0, 0x35, 0x0, 0x31, |
|
0x0, 0x36, 0x0, 0x31, |
|
0x0, 0x37, 0x0, 0x31, |
|
0x0, 0x38, 0x0, 0x31, |
|
0x0, 0x39, 0x0, 0x31, |
|
0x0, 0x61, 0x0, 0x31, |
|
0x0, 0x62, 0x0, 0x31, |
|
0x0, 0x63, 0x0, 0x31, |
|
0x0, 0x64, 0x0, 0x31, |
|
0x0, 0x65, 0x0, 0x31, |
|
0x0, 0x66, 0x0, 0x31, |
|
0x0, 0x30, 0x0, 0x32, |
|
0x0, 0x31, 0x0, 0x32, |
|
0x0, 0x32, 0x0, 0x32, |
|
0x0, 0x33, 0x0, 0x32, |
|
0x0, 0x34, 0x0, 0x32, |
|
0x0, 0x35, 0x0, 0x32, |
|
0x0, 0x36, 0x0, 0x32, |
|
0x0, 0x37, 0x0, 0x32, |
|
0x0, 0x38, 0x0, 0x32, |
|
0x0, 0x39, 0x0, 0x32, |
|
0x0, 0x61, 0x0, 0x32, |
|
0x0, 0x62, 0x0, 0x32, |
|
0x0, 0x63, 0x0, 0x32, |
|
0x0, 0x64, 0x0, 0x32, |
|
0x0, 0x65, 0x0, 0x32, |
|
0x0, 0x66, 0x0, 0x32, |
|
0x0, 0x30, 0x0, 0x33, |
|
0x0, 0x31, 0x0, 0x33, |
|
0x0, 0x32, 0x0, 0x33, |
|
0x0, 0x33, 0x0, 0x33, |
|
0x0, 0x34, 0x0, 0x33, |
|
0x0, 0x35, 0x0, 0x33, |
|
0x0, 0x36, 0x0, 0x33, |
|
0x0, 0x37, 0x0, 0x33, |
|
0x0, 0x38, 0x0, 0x33, |
|
0x0, 0x39, 0x0, 0x33, |
|
0x0, 0x61, 0x0, 0x33, |
|
0x0, 0x62, 0x0, 0x33, |
|
0x0, 0x63, 0x0, 0x33, |
|
0x0, 0x64, 0x0, 0x33, |
|
0x0, 0x65, 0x0, 0x33, |
|
0x0, 0x66, 0x0, 0x33, |
|
0x0, 0x30, 0x0, 0x34, |
|
0x0, 0x31, 0x0, 0x34, |
|
0x0, 0x32, 0x0, 0x34, |
|
0x0, 0x33, 0x0, 0x34, |
|
0x0, 0x34, 0x0, 0x34, |
|
0x0, 0x35, 0x0, 0x34, |
|
0x0, 0x36, 0x0, 0x34, |
|
0x0, 0x37, 0x0, 0x34, |
|
0x0, 0x38, 0x0, 0x34, |
|
0x0, 0x39, 0x0, 0x34, |
|
0x0, 0x61, 0x0, 0x34, |
|
0x0, 0x62, 0x0, 0x34, |
|
0x0, 0x63, 0x0, 0x34, |
|
0x0, 0x64, 0x0, 0x34, |
|
0x0, 0x65, 0x0, 0x34, |
|
0x0, 0x66, 0x0, 0x34, |
|
0x0, 0x30, 0x0, 0x35, |
|
0x0, 0x31, 0x0, 0x35, |
|
0x0, 0x32, 0x0, 0x35, |
|
0x0, 0x33, 0x0, 0x35, |
|
0x0, 0x34, 0x0, 0x35, |
|
0x0, 0x35, 0x0, 0x35, |
|
0x0, 0x36, 0x0, 0x35, |
|
0x0, 0x37, 0x0, 0x35, |
|
0x0, 0x38, 0x0, 0x35, |
|
0x0, 0x39, 0x0, 0x35, |
|
0x0, 0x61, 0x0, 0x35, |
|
0x0, 0x62, 0x0, 0x35, |
|
0x0, 0x63, 0x0, 0x35, |
|
0x0, 0x64, 0x0, 0x35, |
|
0x0, 0x65, 0x0, 0x35, |
|
0x0, 0x66, 0x0, 0x35, |
|
0x0, 0x30, 0x0, 0x36, |
|
0x0, 0x31, 0x0, 0x36, |
|
0x0, 0x32, 0x0, 0x36, |
|
0x0, 0x33, 0x0, 0x36, |
|
0x0, 0x34, 0x0, 0x36, |
|
0x0, 0x35, 0x0, 0x36, |
|
0x0, 0x36, 0x0, 0x36, |
|
0x0, 0x37, 0x0, 0x36, |
|
0x0, 0x38, 0x0, 0x36, |
|
0x0, 0x39, 0x0, 0x36, |
|
0x0, 0x61, 0x0, 0x36, |
|
0x0, 0x62, 0x0, 0x36, |
|
0x0, 0x63, 0x0, 0x36, |
|
0x0, 0x64, 0x0, 0x36, |
|
0x0, 0x65, 0x0, 0x36, |
|
0x0, 0x66, 0x0, 0x36, |
|
0x0, 0x30, 0x0, 0x37, |
|
0x0, 0x31, 0x0, 0x37, |
|
0x0, 0x32, 0x0, 0x37, |
|
0x0, 0x33, 0x0, 0x37, |
|
0x0, 0x34, 0x0, 0x37, |
|
0x0, 0x35, 0x0, 0x37, |
|
0x0, 0x36, 0x0, 0x37, |
|
0x0, 0x37, 0x0, 0x37, |
|
0x0, 0x38, 0x0, 0x37, |
|
0x0, 0x39, 0x0, 0x37, |
|
0x0, 0x61, 0x0, 0x37, |
|
0x0, 0x62, 0x0, 0x37, |
|
0x0, 0x63, 0x0, 0x37, |
|
0x0, 0x64, 0x0, 0x37, |
|
0x0, 0x65, 0x0, 0x37, |
|
0x0, 0x66, 0x0, 0x37, |
|
0x0, 0x30, 0x0, 0x38, |
|
0x0, 0x31, 0x0, 0x38, |
|
0x0, 0x32, 0x0, 0x38, |
|
0x0, 0x33, 0x0, 0x38, |
|
0x0, 0x34, 0x0, 0x38, |
|
0x0, 0x35, 0x0, 0x38, |
|
0x0, 0x36, 0x0, 0x38, |
|
0x0, 0x37, 0x0, 0x38, |
|
0x0, 0x38, 0x0, 0x38, |
|
0x0, 0x39, 0x0, 0x38, |
|
0x0, 0x61, 0x0, 0x38, |
|
0x0, 0x62, 0x0, 0x38, |
|
0x0, 0x63, 0x0, 0x38, |
|
0x0, 0x64, 0x0, 0x38, |
|
0x0, 0x65, 0x0, 0x38, |
|
0x0, 0x66, 0x0, 0x38, |
|
0x0, 0x30, 0x0, 0x39, |
|
0x0, 0x31, 0x0, 0x39, |
|
0x0, 0x32, 0x0, 0x39, |
|
0x0, 0x33, 0x0, 0x39, |
|
0x0, 0x34, 0x0, 0x39, |
|
0x0, 0x35, 0x0, 0x39, |
|
0x0, 0x36, 0x0, 0x39, |
|
0x0, 0x37, 0x0, 0x39, |
|
0x0, 0x38, 0x0, 0x39, |
|
0x0, 0x39, 0x0, 0x39, |
|
0x0, 0x61, 0x0, 0x39, |
|
0x0, 0x62, 0x0, 0x39, |
|
0x0, 0x63, 0x0, 0x39, |
|
0x0, 0x64, 0x0, 0x39, |
|
0x0, 0x65, 0x0, 0x39, |
|
0x0, 0x66, 0x0, 0x39, |
|
0x0, 0x30, 0x0, 0x61, |
|
0x0, 0x31, 0x0, 0x61, |
|
0x0, 0x32, 0x0, 0x61, |
|
0x0, 0x33, 0x0, 0x61, |
|
0x0, 0x34, 0x0, 0x61, |
|
0x0, 0x35, 0x0, 0x61, |
|
0x0, 0x36, 0x0, 0x61, |
|
0x0, 0x37, 0x0, 0x61, |
|
0x0, 0x38, 0x0, 0x61, |
|
0x0, 0x39, 0x0, 0x61, |
|
0x0, 0x61, 0x0, 0x61, |
|
0x0, 0x62, 0x0, 0x61, |
|
0x0, 0x63, 0x0, 0x61, |
|
0x0, 0x64, 0x0, 0x61, |
|
0x0, 0x65, 0x0, 0x61, |
|
0x0, 0x66, 0x0, 0x61, |
|
0x0, 0x30, 0x0, 0x62, |
|
0x0, 0x31, 0x0, 0x62, |
|
0x0, 0x32, 0x0, 0x62, |
|
0x0, 0x33, 0x0, 0x62, |
|
0x0, 0x34, 0x0, 0x62, |
|
0x0, 0x35, 0x0, 0x62, |
|
0x0, 0x36, 0x0, 0x62, |
|
0x0, 0x37, 0x0, 0x62, |
|
0x0, 0x38, 0x0, 0x62, |
|
0x0, 0x39, 0x0, 0x62, |
|
0x0, 0x61, 0x0, 0x62, |
|
0x0, 0x62, 0x0, 0x62, |
|
0x0, 0x63, 0x0, 0x62, |
|
0x0, 0x64, 0x0, 0x62, |
|
0x0, 0x65, 0x0, 0x62, |
|
0x0, 0x66, 0x0, 0x62, |
|
0x0, 0x30, 0x0, 0x63, |
|
0x0, 0x31, 0x0, 0x63, |
|
0x0, 0x32, 0x0, 0x63, |
|
0x0, 0x33, 0x0, 0x63, |
|
0x0, 0x34, 0x0, 0x63, |
|
0x0, 0x35, 0x0, 0x63, |
|
0x0, 0x36, 0x0, 0x63, |
|
0x0, 0x37, 0x0, 0x63, |
|
0x0, 0x38, 0x0, 0x63, |
|
0x0, 0x39, 0x0, 0x63, |
|
0x0, 0x61, 0x0, 0x63, |
|
0x0, 0x62, 0x0, 0x63, |
|
0x0, 0x63, 0x0, 0x63, |
|
0x0, 0x64, 0x0, 0x63, |
|
0x0, 0x65, 0x0, 0x63, |
|
0x0, 0x66, 0x0, 0x63, |
|
0x0, 0x30, 0x0, 0x64, |
|
0x0, 0x31, 0x0, 0x64, |
|
0x0, 0x32, 0x0, 0x64, |
|
0x0, 0x33, 0x0, 0x64, |
|
0x0, 0x34, 0x0, 0x64, |
|
0x0, 0x35, 0x0, 0x64, |
|
0x0, 0x36, 0x0, 0x64, |
|
0x0, 0x37, 0x0, 0x64, |
|
0x0, 0x38, 0x0, 0x64, |
|
0x0, 0x39, 0x0, 0x64, |
|
0x0, 0x61, 0x0, 0x64, |
|
0x0, 0x62, 0x0, 0x64, |
|
0x0, 0x63, 0x0, 0x64, |
|
0x0, 0x64, 0x0, 0x64, |
|
0x0, 0x65, 0x0, 0x64, |
|
0x0, 0x66, 0x0, 0x64, |
|
0x0, 0x30, 0x0, 0x65, |
|
0x0, 0x31, 0x0, 0x65, |
|
0x0, 0x32, 0x0, 0x65, |
|
0x0, 0x33, 0x0, 0x65, |
|
0x0, 0x34, 0x0, 0x65, |
|
0x0, 0x35, 0x0, 0x65, |
|
0x0, 0x36, 0x0, 0x65, |
|
0x0, 0x37, 0x0, 0x65, |
|
0x0, 0x38, 0x0, 0x65, |
|
0x0, 0x39, 0x0, 0x65, |
|
0x0, 0x61, 0x0, 0x65, |
|
0x0, 0x62, 0x0, 0x65, |
|
0x0, 0x63, 0x0, 0x65, |
|
0x0, 0x64, 0x0, 0x65, |
|
0x0, 0x65, 0x0, 0x65, |
|
0x0, 0x66, 0x0, 0x65, |
|
0x0, 0x30, 0x0, 0x66, |
|
0x0, 0x31, 0x0, 0x66, |
|
0x0, 0x32, 0x0, 0x66, |
|
0x0, 0x33, 0x0, 0x66, |
|
0x0, 0x34, 0x0, 0x66, |
|
0x0, 0x35, 0x0, 0x66, |
|
0x0, 0x36, 0x0, 0x66, |
|
0x0, 0x37, 0x0, 0x66, |
|
0x0, 0x38, 0x0, 0x66, |
|
0x0, 0x39, 0x0, 0x66, |
|
0x0, 0x61, 0x0, 0x66, |
|
0x0, 0x62, 0x0, 0x66, |
|
0x0, 0x63, 0x0, 0x66, |
|
0x0, 0x64, 0x0, 0x66, |
|
0x0, 0x65, 0x0, 0x66, |
|
0x0, 0x66, 0x0, 0x66 |
|
}; |
|
|
|
#endregion |
|
|
|
#region Init |
|
|
|
private byte[] _bytes; |
|
|
|
public Bench() { |
|
_bytes = new byte[] {0}; |
|
} |
|
|
|
[GlobalSetup] |
|
public void GlobalSetup() |
|
{ |
|
_bytes = new byte[N]; |
|
new Random(42).NextBytes(_bytes); |
|
} |
|
|
|
#endregion |
|
|
|
/// <summary> |
|
/// https://github.com/fit-ctu-discord/honza-botner/blob/45d37346576d254a5acb82dedafd7d6712c1c619/src/HonzaBotner.Services/Sha256HashService.cs |
|
/// by https://github.com/ostorc |
|
/// </summary> |
|
[Benchmark(Baseline = true)] |
|
public string StringBuilderForEachByte() |
|
{ |
|
StringBuilder strB = new(); |
|
foreach (byte b in _bytes) |
|
strB.Append(b.ToString("X2")); |
|
|
|
return strB.ToString(); |
|
} |
|
|
|
/// <summary> |
|
/// https://stackoverflow.com/a/3824807/3161322 |
|
/// by https://stackoverflow.com/users/64084 |
|
/// </summary> |
|
[Benchmark] |
|
public string StringBuilderForEachBytePreAllocated() |
|
{ |
|
StringBuilder hex = new StringBuilder(_bytes.Length * 2); |
|
foreach (byte b in _bytes) |
|
hex.Append(b.ToString("X2")); |
|
return hex.ToString(); |
|
} |
|
|
|
/// <summary> |
|
/// unknown, probably modified |
|
/// https://stackoverflow.com/a/3824807/3161322 |
|
/// by https://stackoverflow.com/users/64084 |
|
/// </summary> |
|
[Benchmark] |
|
public string StringBuilderForEachAppendFormat() |
|
{ |
|
StringBuilder hex = new StringBuilder(_bytes.Length * 2); |
|
foreach (byte b in _bytes) |
|
hex.AppendFormat("{0:X2}", b); |
|
return hex.ToString(); |
|
} |
|
|
|
/// <summary> |
|
/// https://stackoverflow.com/a/311179/3161322 |
|
/// by https://stackoverflow.com/users/18771/tomalak |
|
/// </summary> |
|
[Benchmark] |
|
public string BitConverterReplace() |
|
{ |
|
string hex = BitConverter.ToString(_bytes); |
|
return hex.Replace("-", ""); |
|
} |
|
|
|
/// <summary> |
|
/// https://stackoverflow.com/a/311382/3161322 |
|
/// by https://stackoverflow.com/users/987/will-dean |
|
/// </summary> |
|
[Benchmark] |
|
public string StringJoinArrayConvertAll() |
|
=> string.Join(string.Empty, Array.ConvertAll(_bytes, b => b.ToString("X2"))); |
|
|
|
/// <summary> |
|
/// https://stackoverflow.com/a/2345722/3161322 |
|
/// by |
|
/// </summary> |
|
[Benchmark] |
|
public string StringJoinSelect() |
|
=> string.Join(string.Empty, _bytes.Select(bin => bin.ToString("X2")).ToArray()); |
|
|
|
/// <summary> |
|
/// https://stackoverflow.com/a/311382/3161322 |
|
/// by https://stackoverflow.com/users/987/will-dean |
|
/// </summary> |
|
[Benchmark] |
|
public string StringConcatArrayConvertAll() |
|
=> string.Concat(Array.ConvertAll(_bytes, b => b.ToString("X2"))); |
|
|
|
/// <summary> |
|
/// https://stackoverflow.com/a/311382/3161322 |
|
/// by https://stackoverflow.com/users/149265/allon-guralnek |
|
/// </summary> |
|
[Benchmark] |
|
public string StringConcatSelect() |
|
=> string.Concat(_bytes.Select(b => b.ToString("X2"))); |
|
|
|
/// <summary> |
|
/// https://stackoverflow.com/a/3824807/3161322 |
|
/// by https://stackoverflow.com/users/64084 |
|
/// </summary> |
|
[Benchmark] |
|
public string StringBuilderAggregateBytesAppend() |
|
=> _bytes.Aggregate(new StringBuilder(_bytes.Length * 2), (sb, b) => sb.Append(b.ToString("X2"))).ToString(); |
|
|
|
/// <summary> |
|
/// unknown, probably modified |
|
/// https://stackoverflow.com/a/3824807/3161322 |
|
/// by https://stackoverflow.com/users/64084 |
|
/// </summary> |
|
[Benchmark] |
|
public string StringBuilderAggregateBytesAppendFormat() |
|
=> _bytes.Aggregate(new StringBuilder(_bytes.Length * 2), (sb, b) => sb.AppendFormat("{0:X2}", b)).ToString(); |
|
|
|
/// <summary> |
|
/// https://stackoverflow.com/a/632920/3161322 |
|
/// by https://stackoverflow.com/users/676066 |
|
/// </summary> |
|
[Benchmark] |
|
public string ByteManipulationHexMultiply() |
|
{ |
|
char[] c = new char[_bytes.Length * 2]; |
|
byte b; |
|
for (int i = 0; i < _bytes.Length; i++) |
|
{ |
|
b = ((byte)(_bytes[i] >> 4)); |
|
c[i * 2] = (char)(b > 9 ? b + 0x37 : b + 0x30); |
|
b = ((byte)(_bytes[i] & 0xF)); |
|
c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30); |
|
} |
|
|
|
return new string(c); |
|
} |
|
|
|
/// <summary> |
|
/// https://stackoverflow.com/a/3974535/3161322 |
|
/// by https://stackoverflow.com/users/21784/kgriffs |
|
/// </summary> |
|
[Benchmark] |
|
public string ByteManipulationHexIncrement() |
|
{ |
|
char[] c = new char[_bytes.Length * 2]; |
|
byte b; |
|
for (int bx = 0, cx = 0; bx < _bytes.Length; ++bx, ++cx) |
|
{ |
|
b = ((byte)(_bytes[bx] >> 4)); |
|
c[cx] = (char)(b > 9 ? b + 0x37 + 0x20 : b + 0x30); |
|
b = ((byte)(_bytes[bx] & 0x0F)); |
|
c[++cx] = (char)(b > 9 ? b + 0x37 + 0x20 : b + 0x30); |
|
} |
|
|
|
return new string(c); |
|
} |
|
|
|
/// <summary> |
|
/// https://stackoverflow.com/a/14333437/3161322 |
|
/// by https://stackoverflow.com/users/445517 |
|
/// </summary> |
|
[Benchmark] |
|
public string ByteManipulationDecimal() |
|
{ |
|
char[] c = new char[_bytes.Length * 2]; |
|
int b; |
|
for (int i = 0; i < _bytes.Length; i++) |
|
{ |
|
b = _bytes[i] >> 4; |
|
c[i * 2] = (char)(55 + b + (((b - 10) >> 31) & -7)); |
|
b = _bytes[i] & 0xF; |
|
c[i * 2 + 1] = (char)(55 + b + (((b - 10) >> 31) & -7)); |
|
} |
|
|
|
return new string(c); |
|
} |
|
|
|
/// <summary> |
|
/// https://stackoverflow.com/a/22158486/3161322 |
|
/// by https://stackoverflow.com/users/278889/patrick |
|
/// </summary> |
|
[Benchmark] |
|
public string WhileLocalLookup() |
|
{ |
|
char[] lookup = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; |
|
int i = -1, p = 0, l = _bytes.Length; |
|
char[] c = new char[l * 2]; |
|
byte d; |
|
--l; |
|
--p; |
|
while (i < l) |
|
{ |
|
d = _bytes[++i]; |
|
c[++p] = lookup[d >> 4]; |
|
c[++p] = lookup[d & 0xF]; |
|
} |
|
|
|
return new string(c, 0, c.Length); |
|
} |
|
|
|
/// <summary> |
|
/// based on WhileLocalLookup |
|
/// by https://github.com/antoninkriz |
|
/// </summary> |
|
[Benchmark] |
|
public string WhilePropertyLookup() |
|
{ |
|
int i = -1, p = 0, l = _bytes.Length; |
|
char[] c = new char[l * 2]; |
|
byte d; |
|
--l; |
|
--p; |
|
while (i < l) |
|
{ |
|
d = _bytes[++i]; |
|
c[++p] = HexAlphabetArray[d >> 4]; |
|
c[++p] = HexAlphabetArray[d & 0xF]; |
|
} |
|
|
|
return new string(c, 0, c.Length); |
|
} |
|
|
|
/// <summary> |
|
/// https://docs.microsoft.com/en-us/archive/blogs/blambert/blambertcodesnip-fast-byte-array-to-hex-string-conversion |
|
/// by Brian Lambert |
|
/// </summary> |
|
[Benchmark] |
|
public string LookupPerNibble() |
|
{ |
|
StringBuilder result = new StringBuilder(_bytes.Length * 2); |
|
foreach (byte b in _bytes) |
|
{ |
|
result.Append(HexStringTable[b]); |
|
} |
|
|
|
return result.ToString(); |
|
} |
|
|
|
/// <summary> |
|
/// https://stackoverflow.com/a/5919521/3161322 |
|
/// by https://stackoverflow.com/users/610692/nathan-moinvaziri |
|
/// </summary> |
|
[Benchmark] |
|
public string LookupAndShift() |
|
{ |
|
StringBuilder result = new StringBuilder(_bytes.Length * 2); |
|
foreach (byte b in _bytes) |
|
{ |
|
result.Append(HexAlphabetString[b >> 4]); |
|
result.Append(HexAlphabetString[b & 0xF]); |
|
} |
|
|
|
return result.ToString(); |
|
} |
|
|
|
/// <summary> |
|
/// - |
|
/// by https://github.com/antoninkriz |
|
/// </summary> |
|
[Benchmark] |
|
public string LookupAndShiftAlphabetArray() |
|
{ |
|
var res = _bytes.Length * 2 <= 1024 ? stackalloc char[_bytes.Length * 2] : new char[_bytes.Length * 2]; |
|
|
|
for (int i = 0, j = 0; i < _bytes.Length; ++i, ++j) |
|
{ |
|
res[j] = HexAlphabetArray[_bytes[i] >> 4]; |
|
res[++j] = HexAlphabetArray[_bytes[i] & 0xF]; |
|
} |
|
|
|
return new string(res); |
|
} |
|
|
|
/// <summary> |
|
/// - |
|
/// by https://github.com/antoninkriz |
|
/// </summary> |
|
[Benchmark] |
|
public string LookupAndShiftAlphabetSpan() |
|
{ |
|
var res = _bytes.Length * 2 <= 1024 ? stackalloc char[_bytes.Length * 2] : new char[_bytes.Length * 2]; |
|
|
|
for (int i = 0, j = 0; i < _bytes.Length; ++i, ++j) |
|
{ |
|
res[j] = (char)HexAlphabetSpan[_bytes[i] >> 4]; |
|
res[++j] = (char)HexAlphabetSpan[_bytes[i] & 0xF]; |
|
} |
|
|
|
return new string(res); |
|
} |
|
|
|
/// <summary> |
|
/// - |
|
/// by https://github.com/antoninkriz |
|
/// </summary> |
|
[Benchmark] |
|
public string LookupAndShiftAlphabetSpanMultiply() |
|
{ |
|
var res = _bytes.Length * 2 <= 1024 ? stackalloc char[_bytes.Length * 2] : new char[_bytes.Length * 2]; |
|
|
|
for (var i = 0; i < _bytes.Length; ++i) |
|
{ |
|
var j = i * 2; |
|
res[j] = (char)HexAlphabetSpan[_bytes[i] >> 4]; |
|
res[j + 1] = (char)HexAlphabetSpan[_bytes[i] & 0xF]; |
|
} |
|
|
|
return new string(res); |
|
} |
|
|
|
/// <summary> |
|
/// http://stackoverflow.com/a/24343727/48700 |
|
/// by https://stackoverflow.com/users/445517 |
|
/// </summary> |
|
[Benchmark] |
|
public string LookupPerByte() |
|
{ |
|
var result = new char[_bytes.Length * 2]; |
|
for (int i = 0; i < _bytes.Length; i++) |
|
{ |
|
var val = Lookup32[_bytes[i]]; |
|
result[2 * i] = (char)val; |
|
result[2 * i + 1] = (char)(val >> 16); |
|
} |
|
|
|
return new string(result); |
|
} |
|
|
|
/// <summary> |
|
/// based on LookupPerByte |
|
/// by https://github.com/antoninkriz |
|
/// </summary> |
|
[Benchmark] |
|
public string LookupPerByteSpan() |
|
{ |
|
var result = _bytes.Length * 2 <= 1024 ? stackalloc char[_bytes.Length * 2] : new char[_bytes.Length * 2]; |
|
for (var i = 0; i < _bytes.Length; i++) |
|
{ |
|
var val = Lookup32[_bytes[i]]; |
|
result[2 * i] = (char)val; |
|
result[2 * i + 1] = (char)(val >> 16); |
|
} |
|
|
|
return new string(result); |
|
} |
|
|
|
/// <summary> |
|
/// based on LookupPerByte |
|
/// by https://github.com/antoninkriz |
|
/// </summary> |
|
[Benchmark] |
|
public string LookupSpanPerByteSpan() |
|
{ |
|
unchecked { |
|
var result = _bytes.Length * 2 <= 1024 ? stackalloc char[_bytes.Length * 2] : new char[_bytes.Length * 2]; |
|
for (var i = 0; i < _bytes.Length; i++) |
|
{ |
|
var val = Unsafe.ReadUnaligned<uint>(ref MemoryMarshal.GetReference(Lookup32Span.Slice(_bytes[i] * 4, 4))); |
|
result[2 * i] = (char) val; |
|
result[2 * i + 1] = (char)(val >> 16); |
|
} |
|
|
|
return new string(result); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// https://stackoverflow.com/a/24343727/3161322 |
|
/// by https://stackoverflow.com/users/445517 |
|
/// </summary> |
|
[Benchmark] |
|
public unsafe string Lookup32UnsafeDirect() |
|
{ |
|
var lookupP = Lookup32UnsafeP; |
|
var result = new string((char)0, _bytes.Length * 2); |
|
fixed (byte* bytesP = _bytes) |
|
fixed (char* resultP = result) |
|
{ |
|
uint* resultP2 = (uint*)resultP; |
|
for (int i = 0; i < _bytes.Length; i++) |
|
{ |
|
resultP2[i] = lookupP[bytesP[i]]; |
|
} |
|
} |
|
|
|
return result; |
|
} |
|
|
|
/// <summary> |
|
/// https://stackoverflow.com/a/24343727/3161322 |
|
/// by https://stackoverflow.com/users/445517 |
|
/// </summary> |
|
[Benchmark] |
|
public unsafe string Lookup32SpanUnsafeDirect() |
|
{ |
|
var result = new string((char)0, _bytes.Length * 2); |
|
fixed (byte* lookupP = Lookup32Span) |
|
fixed (byte* bytesP = _bytes) |
|
fixed (char* resultP = result) |
|
{ |
|
uint* resultP2 = (uint*)resultP; |
|
uint* lookupP2 = (uint*)lookupP; |
|
for (int i = 0; i < _bytes.Length; i++) |
|
{ |
|
resultP2[i] = lookupP2[bytesP[i]]; |
|
} |
|
} |
|
|
|
return result; |
|
} |
|
|
|
/// <summary> |
|
/// https://stackoverflow.com/a/68596484/3161322 |
|
/// by https://stackoverflow.com/posts/68596484/revisions |
|
/// </summary> |
|
[Benchmark] |
|
public string ConvertToHexString() |
|
=> Convert.ToHexString(_bytes); |
|
} |
|
|
|
internal static class Program |
|
{ |
|
public static void Main() |
|
{ |
|
var summary = BenchmarkRunner.Run<Bench>(); |
|
Console.WriteLine(summary); |
|
} |
|
} |
@ruicaramalho Since you're using .Net Framework and not .NET you should use
res.ToString()
instead ofnew string(res)
.You might also need to install NuGet package
System.Memory
to supportSystem.Span
.Be aware that this code was tested using
.NET
and not.Net Framework
so there might (or might not) be some difference in the performance.