Skip to content

Instantly share code, notes, and snippets.

@JamesNK
Created February 19, 2020 05:08
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 JamesNK/395272054c6a1a6404653e16aadc67b5 to your computer and use it in GitHub Desktop.
Save JamesNK/395272054c6a1a6404653e16aadc67b5 to your computer and use it in GitHub Desktop.
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
static class Program
{
static void Main()
{
BenchmarkRunner.Run<Foo>();
}
}
[SimpleJob(RuntimeMoniker.NetCoreApp31)]
public class Foo
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void EncodeBaselineUTF8(ReadOnlySpan<char> chars, Span<byte> bytes)
=> Encoding.UTF8.GetBytes(chars, bytes);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void EncodeBaselineASCII(ReadOnlySpan<char> chars, Span<byte> bytes)
=> Encoding.ASCII.GetBytes(chars, bytes);
private static void EncodeViaNarrow(ReadOnlySpan<char> chars, Span<byte> bytes)
{
if (Vector.IsHardwareAccelerated & chars.Length >= Vector<byte>.Count)
{
var vSource = MemoryMarshal.Cast<char, Vector<ushort>>(chars);
var vDest = MemoryMarshal.Cast<byte, Vector<byte>>(bytes);
int toIndex = 0;
for (int i = 0; i < vSource.Length;)
{
vDest[toIndex++] = Vector.Narrow(vSource[i++], vSource[i++]);
}
chars = chars.Slice(toIndex * Vector<byte>.Count);
bytes = bytes.Slice(toIndex * Vector<byte>.Count);
}
// anything left (or everything if no SIMD)
Encoding.UTF8.GetBytes(chars, bytes);
}
private static unsafe void EncodeViaNarrowUnsafe(string s, Span<byte> bytes)
{
fixed (char* pChars = s)
fixed (byte* pBytes = bytes)
{
EncodeViaNarrow(pChars, pBytes, (uint)s.Length);
}
}
private static unsafe void EncodeViaLoop(string s, Span<byte> bytes)
{
for (var currentOffset = 0; currentOffset < s.Length; currentOffset++)
{
bytes[currentOffset] = (byte)s[currentOffset];
}
}
private static unsafe void EncodeViaUnsafeLoop(string s, Span<byte> bytes)
{
var length = (uint)s.Length;
fixed (char* pChars = s)
fixed (byte* pBytes = bytes)
{
for (var currentOffset = 0U; currentOffset < length; currentOffset++)
{
pBytes[currentOffset] = (byte)pChars[currentOffset];
}
}
}
private static unsafe void EncodeViaNarrow(char* pUtf16Buffer, byte* pAsciiBuffer, uint length)
{
uint currentOffset = 0;
if (Vector.IsHardwareAccelerated & length >= Vector<byte>.Count)
{
uint SizeOfVector = (uint)Unsafe.SizeOf<Vector<byte>>(); // JIT will make this a const
uint finalOffsetWhereCanLoop = length - 2 * SizeOfVector;
do
{
Vector<ushort> utf16VectorHigh = Unsafe.ReadUnaligned<Vector<ushort>>(pUtf16Buffer + currentOffset);
Vector<ushort> utf16VectorLow = Unsafe.ReadUnaligned<Vector<ushort>>(pUtf16Buffer + currentOffset + Vector<ushort>.Count);
Vector<byte> asciiVector = Vector.Narrow(utf16VectorHigh, utf16VectorLow);
Unsafe.WriteUnaligned<Vector<byte>>(pAsciiBuffer + currentOffset, asciiVector);
currentOffset += SizeOfVector;
} while (currentOffset <= finalOffsetWhereCanLoop);
}
for (; currentOffset < length; currentOffset++)
{
pAsciiBuffer[currentOffset] = (byte)pUtf16Buffer[currentOffset];
}
}
private byte[] Buffer = new byte[LoremIpsum.Length];
[Benchmark]
public void SmallBaselineUTF8() => EncodeBaselineUTF8("hello world", Buffer);
[Benchmark]
public void LargeBaselineUTF8() => EncodeBaselineUTF8(LoremIpsum, Buffer);
[Benchmark]
public void SmallBaselineASCII() => EncodeBaselineASCII("hello world", Buffer);
[Benchmark]
public void LargeBaselineASCII() => EncodeBaselineASCII(LoremIpsum, Buffer);
[Benchmark]
public void SmallNarrow() => EncodeViaNarrow("hello world", Buffer);
[Benchmark]
public void LargeNarrow() => EncodeViaNarrow(LoremIpsum, Buffer);
[Benchmark]
public void SmallUnsafeNarrow() => EncodeViaNarrowUnsafe("hello world", Buffer);
[Benchmark]
public void LargeUnsafeNarrow() => EncodeViaNarrowUnsafe(LoremIpsum, Buffer);
[Benchmark]
public void SmallLoop() => EncodeViaLoop("hello world", Buffer);
[Benchmark]
public void LargeLoop() => EncodeViaLoop(LoremIpsum, Buffer);
[Benchmark]
public void SmallUnsafeLoop() => EncodeViaUnsafeLoop("hello world", Buffer);
[Benchmark]
public void LargeUnsafeLoop() => EncodeViaUnsafeLoop(LoremIpsum, Buffer);
const string LoremIpsum = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non nibh ac nisi faucibus pulvinar ac nec augue. Nullam ac tincidunt ex. Suspendisse et facilisis nibh. Aenean fermentum ullamcorper sagittis. Pellentesque id dui ut odio laoreet pellentesque vitae sit amet orci. Praesent vel interdum velit. In enim urna, auctor et sodales in, vestibulum in tortor. Integer molestie ac diam ac dictum.
Curabitur rutrum fringilla velit id ornare. Donec elit risus, fermentum a massa eu, facilisis fermentum turpis. Nulla facilisi. Phasellus odio magna, convallis quis odio eget, placerat sollicitudin erat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin mollis justo dui. Nulla ut sapien orci. Proin eu faucibus nisi.
In hac habitasse platea dictumst. Phasellus non condimentum arcu, non lacinia tortor. Nulla commodo ullamcorper tortor in lobortis. Aliquam convallis elit turpis, non lacinia purus iaculis ut. Integer euismod at leo nec pellentesque. Nullam a viverra nibh, in sollicitudin mi. Phasellus gravida iaculis felis, nec volutpat justo faucibus ut. Aliquam erat volutpat. Praesent nec imperdiet turpis. Duis malesuada odio quis massa venenatis semper. Proin iaculis nibh eget dapibus gravida. Sed mollis neque turpis, eget pellentesque lectus suscipit eget. Pellentesque at gravida orci, non ullamcorper ipsum. Sed aliquet dui vitae nunc fringilla dapibus.
Ut vel ultrices ante, non ullamcorper dolor. Nulla convallis augue nec erat vulputate varius. Ut euismod porta mauris vitae dapibus. Donec hendrerit sapien vel iaculis tristique. Ut sodales dignissim arcu, at commodo risus. Aliquam eget ornare urna. Duis porttitor, dui sed accumsan malesuada, ante arcu accumsan felis, a pulvinar augue felis a ligula. Curabitur dapibus, orci lobortis fringilla ornare, ipsum erat interdum elit, in dapibus lectus risus et augue. Donec at nulla ut risus mattis varius. Mauris sit amet leo vitae enim pharetra porttitor ac sit amet ante. Nam ac risus sem. Maecenas dictum nec est in tristique. Proin consequat nulla eu ex congue aliquam.
Nullam ut ornare diam. Morbi hendrerit porttitor laoreet. In ornare dui neque, pulvinar molestie tortor bibendum quis. Maecenas sollicitudin suscipit sapien, nec viverra tortor volutpat ac. Suspendisse mattis consequat velit, sit amet scelerisque nibh maximus non. Suspendisse lacinia sit amet velit eget vestibulum. Vestibulum vestibulum condimentum consectetur. Duis sollicitudin lorem magna, at consequat nulla sodales ac. Nunc tristique, dolor a scelerisque viverra, nunc magna lacinia mauris, a posuere eros erat eget nisl. Mauris ligula massa, facilisis a tortor id, tincidunt volutpat massa. In odio ex, blandit nec eros sed, aliquam ultrices ante. Pellentesque libero nisl, sodales in nulla quis, auctor tristique velit. Proin ut tempor lorem.";
}
Method Mean Error StdDev
SmallBaselineUTF8 101.946 ns 0.8025 ns 0.7114 ns
LargeBaselineUTF8 246.103 ns 4.2770 ns 3.5715 ns
SmallBaselineASCII 97.148 ns 0.3960 ns 0.3307 ns
LargeBaselineASCII 274.668 ns 1.5462 ns 1.3707 ns
SmallNarrow 107.201 ns 0.2676 ns 0.2235 ns
LargeNarrow 365.003 ns 3.1617 ns 2.8028 ns
SmallUnsafeNarrow 13.170 ns 0.2982 ns 0.6609 ns
LargeUnsafeNarrow 311.244 ns 2.2004 ns 1.9506 ns
SmallLoop 11.882 ns 0.2684 ns 0.3395 ns
LargeLoop 2,883.695 ns 52.6686 ns 41.1201 ns
SmallUnsafeLoop 8.205 ns 0.1956 ns 0.1921 ns
LargeUnsafeLoop 1,277.362 ns 24.5696 ns 26.2892 ns
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment