Skip to content

Instantly share code, notes, and snippets.

@Ruikuan
Last active October 8, 2019 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ruikuan/1066fb7af4deb0f6d50319ef1d98717b to your computer and use it in GitHub Desktop.
Save Ruikuan/1066fb7af4deb0f6d50319ef1d98717b to your computer and use it in GitHub Desktop.
CorrelationIdGenerator.GenerateId Benchmark
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System;
using System.Runtime.InteropServices;
namespace GenerateIdBenchmark
{
class Program
{
static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<Generator>();
}
}
[MemoryDiagnoser]
public class Generator
{
private readonly static long _id = DateTime.UtcNow.Ticks;
[Benchmark(Baseline = true)]
public string GenerateId_Origin() => CorrelationIdGenerator_Origin.GenerateId(_id);
[Benchmark]
public string GenerateId() => CorrelationIdGenerator.GenerateId(_id);
}
internal static class CorrelationIdGenerator_Origin
{
// Base32 encoding - in ascii sort order for easy text based sorting
private static readonly char[] s_encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV".ToCharArray();
public static string GenerateId(long id)
{
return string.Create(13, id, (buffer, value) =>
{
char[] encode32Chars = s_encode32Chars;
buffer[12] = encode32Chars[value & 31];
buffer[11] = encode32Chars[(value >> 5) & 31];
buffer[10] = encode32Chars[(value >> 10) & 31];
buffer[9] = encode32Chars[(value >> 15) & 31];
buffer[8] = encode32Chars[(value >> 20) & 31];
buffer[7] = encode32Chars[(value >> 25) & 31];
buffer[6] = encode32Chars[(value >> 30) & 31];
buffer[5] = encode32Chars[(value >> 35) & 31];
buffer[4] = encode32Chars[(value >> 40) & 31];
buffer[3] = encode32Chars[(value >> 45) & 31];
buffer[2] = encode32Chars[(value >> 50) & 31];
buffer[1] = encode32Chars[(value >> 55) & 31];
buffer[0] = encode32Chars[(value >> 60) & 31];
});
}
}
internal static class CorrelationIdGenerator
{
// Base32 encoding - in ascii sort order for easy text based sorting
// 0123456789ABCDEFGHIJKLMNOPQRSTUV
// use this optimization: https://github.com/dotnet/roslyn/pull/24621
private static ReadOnlySpan<byte> s_encode32Bytes => new byte[] { (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', (byte)'G', (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', (byte)'V' };
public unsafe static string GenerateId(long id)
{
return string.Create(13, id, (buffer, value) =>
{
Span<byte> bufferBytes = MemoryMarshal.Cast<char, byte>(buffer);
// ReadOnlySpan<byte> doesn't support long index, have to use pointer to avoid cast long to int.
fixed (byte* encode32Bytes = s_encode32Bytes)
{
if (BitConverter.IsLittleEndian)
{
bufferBytes[24] = encode32Bytes[value & 31];
bufferBytes[22] = encode32Bytes[(value >> 5) & 31];
bufferBytes[20] = encode32Bytes[(value >> 10) & 31];
bufferBytes[18] = encode32Bytes[(value >> 15) & 31];
bufferBytes[16] = encode32Bytes[(value >> 20) & 31];
bufferBytes[14] = encode32Bytes[(value >> 25) & 31];
bufferBytes[12] = encode32Bytes[(value >> 30) & 31];
bufferBytes[10] = encode32Bytes[(value >> 35) & 31];
bufferBytes[8] = encode32Bytes[(value >> 40) & 31];
bufferBytes[6] = encode32Bytes[(value >> 45) & 31];
bufferBytes[4] = encode32Bytes[(value >> 50) & 31];
bufferBytes[2] = encode32Bytes[(value >> 55) & 31];
bufferBytes[0] = encode32Bytes[(value >> 60) & 31];
}
else
{
bufferBytes[25] = encode32Bytes[value & 31];
bufferBytes[23] = encode32Bytes[(value >> 5) & 31];
bufferBytes[21] = encode32Bytes[(value >> 10) & 31];
bufferBytes[19] = encode32Bytes[(value >> 15) & 31];
bufferBytes[17] = encode32Bytes[(value >> 20) & 31];
bufferBytes[15] = encode32Bytes[(value >> 25) & 31];
bufferBytes[13] = encode32Bytes[(value >> 30) & 31];
bufferBytes[11] = encode32Bytes[(value >> 35) & 31];
bufferBytes[9] = encode32Bytes[(value >> 40) & 31];
bufferBytes[7] = encode32Bytes[(value >> 45) & 31];
bufferBytes[5] = encode32Bytes[(value >> 50) & 31];
bufferBytes[3] = encode32Bytes[(value >> 55) & 31];
bufferBytes[1] = encode32Bytes[(value >> 60) & 31];
}
}
});
}
}
}
// * Summary *
BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
Intel Core i7-3720QM CPU 2.60GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.0.100
[Host] : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bit RyuJIT
DefaultJob : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bit RyuJIT
| Method | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated |
|------------------ |---------:|----------:|----------:|------:|-------:|------:|------:|----------:|
| GenerateId_Origin | 18.19 ns | 0.1217 ns | 0.1079 ns | 1.00 | 0.0153 | - | - | 48 B |
| GenerateId | 16.50 ns | 0.0786 ns | 0.0697 ns | 0.91 | 0.0153 | - | - | 48 B |
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment