-
-
Save tgsstdio/1ad429532f8b8138ea03 to your computer and use it in GitHub Desktop.
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; | |
using System.Diagnostics; | |
using System.Reflection.Emit; | |
using System.Runtime.InteropServices; | |
using System.Security; | |
// Based on fork and see | |
// http://xoofx.com/blog/2010/10/23/high-performance-memcpy-gotchas-in-c/ | |
namespace TestPerf | |
{ | |
public class UnsafeMemCopy | |
{ | |
[Params(4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384)] | |
public int BlockSize { get; set; } | |
private static unsafe readonly CopyBlockDelegate _cpBlk = GenerateCpBlk(); | |
public unsafe delegate void CopyBlockDelegate(void* des, void* src, uint bytes); | |
[DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false), SuppressUnmanagedCodeSecurity] | |
public static unsafe extern void* memcpy(void* dest, void* src, ulong count); | |
static unsafe void CpBlk(void* dest, void* src, uint count) | |
{ | |
var local = _cpBlk; | |
local(dest, src, count); | |
} | |
static unsafe void Custom(void* dest, void* src, int count) | |
{ | |
var block = count >> 3; | |
var pDest = (long*)dest; | |
var pSrc = (long*)src; | |
for (var i = 0; i < block; i++) | |
{ | |
*pDest = *pSrc; pDest++; pSrc++; | |
} | |
dest = pDest; | |
src = pSrc; | |
count = count - (block << 3); | |
if (count > 0) | |
{ | |
var pDestB = (byte*)dest; | |
var pSrcB = (byte*)src; | |
for (var i = 0; i < count; i++) | |
{ | |
*pDestB = *pSrcB; pDestB++; pSrcB++; | |
} | |
} | |
} | |
static CopyBlockDelegate GenerateCpBlk() | |
{ | |
var method = new DynamicMethod("CopyBlockIL", typeof(void), new[] { typeof(void*), typeof(void*), typeof(uint) }, typeof(UnsafeMemCopy)); | |
var emitter = method.GetILGenerator(); | |
// emit IL | |
emitter.Emit(OpCodes.Ldarg_0); | |
emitter.Emit(OpCodes.Ldarg_1); | |
emitter.Emit(OpCodes.Ldarg_2); | |
emitter.Emit(OpCodes.Cpblk); | |
emitter.Emit(OpCodes.Ret); | |
// compile to delegate | |
return (CopyBlockDelegate)method.CreateDelegate(typeof(CopyBlockDelegate)); | |
} | |
byte[] src; | |
byte[] dest; | |
const int MAX_BUFFER_SIZE = 32768; | |
[Setup] | |
public unsafe void SetupData() | |
{ | |
src = new byte[MAX_BUFFER_SIZE]; | |
for (var i = 0; i < MAX_BUFFER_SIZE; i++) | |
src[i] = (byte)i; | |
dest = new byte[MAX_BUFFER_SIZE]; | |
} | |
[Benchmark] | |
public unsafe void CopyViaBlockCopy() | |
{ | |
fixed (void* pDest = &dest[0]) | |
fixed (void* pSrc = &src[0]) | |
{ | |
Buffer.BlockCopy(src, 0, dest, 0, BlockSize); | |
} | |
} | |
[Benchmark] | |
public unsafe void CopyViaCpBlk() | |
{ | |
fixed (void* pDest = &dest[0]) | |
fixed (void* pSrc = &src[0]) | |
{ | |
CpBlk(pDest, pSrc, (uint)BlockSize); | |
} | |
} | |
[Benchmark] | |
public unsafe void CopyViaMemcpy() | |
{ | |
fixed (void* pDest = &dest[0]) | |
fixed (void* pSrc = &src[0]) | |
{ | |
memcpy(pDest, pSrc, (ulong)BlockSize); | |
} | |
} | |
[Benchmark] | |
public unsafe void CopyViaArrayCopy() | |
{ | |
fixed (void* pDest = &dest[0]) | |
fixed (void* pSrc = &src[0]) | |
{ | |
Array.Copy(src, dest, BlockSize); | |
} | |
} | |
[Benchmark] | |
public unsafe void CopyViaCustomCopy() | |
{ | |
fixed (void* pDest = &dest[0]) | |
fixed (void* pSrc = &src[0]) | |
{ | |
Custom(pDest, pSrc, BlockSize); | |
} | |
} | |
[Benchmark] | |
public unsafe void CopyViaMarshalCopy() | |
{ | |
fixed (void* pDest = &dest[0]) | |
fixed (void* pSrc = &src[0]) | |
{ | |
IntPtr pDestPtr = (IntPtr)pDest; | |
Marshal.Copy(src, 0, pDestPtr, BlockSize); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Preliminary RELEASE results with Benchmark.NET