Skip to content

Instantly share code, notes, and snippets.

@xoofx
Created March 16, 2021 08:15
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 xoofx/4bda0160ff8adc20b3096cbd8270e727 to your computer and use it in GitHub Desktop.
Save xoofx/4bda0160ff8adc20b3096cbd8270e727 to your computer and use it in GitHub Desktop.
Unpack variable 1-4 int
// From Twitter discussion: https://twitter.com/firstdrafthell/status/1371704847783895042
//
// | Method | Mean | Error | StdDev |
// |----------------------------- |----------:|----------:|----------:|
// | TestUnpack | 25.454 ns | 0.1450 ns | 0.1356 ns |
// | TestUnpack2 | 25.033 ns | 0.0853 ns | 0.0798 ns |
// | TestUnpackWithSpan | 6.936 ns | 0.0535 ns | 0.0500 ns |
// | TestUnpackWithSpanStackAlloc | 8.733 ns | 0.1149 ns | 0.1075 ns |
using System;
using System.Buffers.Binary;
using System.IO;
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
public class Program
{
private static readonly BinaryReader Reader = new BinaryReader(new InfiniteStream());
private static int _length;
private static int NextLength() => (_length++ & 3) + 1;
[Benchmark]
public void TestUnpack()
{
Unpack(Reader, NextLength());
}
[Benchmark]
public void TestUnpack2()
{
Unpack2(Reader, NextLength());
}
[Benchmark]
public void TestUnpackWithSpan()
{
UnpackWithSpan(Reader, NextLength());
}
[Benchmark]
public void TestUnpackWithSpanStackAlloc()
{
UnpackWithSpanStackAlloc(Reader, NextLength());
}
public int Unpack2(BinaryReader reader, int length)
{
var result = 0;
for (var offset = 8 * (length - 1); offset >= 0; offset -= 8)
result += reader.ReadByte() << offset;
return result;
}
public static int Unpack(BinaryReader reader, int length)
{
var result = 0;
for (var index = 0; index < length; index++)
{
var offset = 8 * (length - index - 1);
var value = reader.ReadByte();
result |= offset == 0 ? value : value << offset;
}
return result;
}
[SkipLocalsInit]
public static unsafe int UnpackWithSpan(BinaryReader reader, int length)
{
uint data = 0;
reader.Read(new Span<byte>(&data, length));
return (int)((BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(data) : data) >> ((4 - length) << 3));
}
public static int UnpackWithSpanStackAlloc(BinaryReader reader, int length)
{
Span<byte> buffer = stackalloc byte[4];
reader.Read(buffer);
var data = BitConverter.ToUInt32(buffer);
return (int)((BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(data) : data) >> ((4 - length) << 3));
}
private static void Main(string[] args)
{
BenchmarkRunner.Run<Program>();
}
private class InfiniteStream : Stream
{
public override void Flush()
{
}
public override int Read(byte[] buffer, int offset, int count)
{
Position += count;
return count;
}
public override int Read(Span<byte> buffer)
{
Position += buffer.Length;
return buffer.Length;
}
public override long Seek(long offset, SeekOrigin origin)
{
return offset;
}
public override void SetLength(long value)
{
}
public override void Write(byte[] buffer, int offset, int count)
{
}
public override bool CanRead => true;
public override bool CanSeek => true;
public override bool CanWrite => true;
public override long Length => long.MaxValue;
public override long Position { get; set; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment