一般来说,你不能比逐个字段地复制更快。
这个一般。有一个例外:如果您的类是 blittable (因此不包含对非 blittable 类的任何引用)并且它是用 [StructLayout(LayoutKind.Sequential)]
(或[StructLayout(LayoutKind.Explicit)]
,但测试更复杂)声明的,那么您可以固定它
GCHandle handle = GCHandle.Alloc(refToYourClass, GCHandleType.Pinned);
unsafe
从那里打开一个新世界......您可以使用一些不安全的(作为键盘和不安全的“用剪刀运行”)代码直接逐字节复制课程的内容。
但要让这个工作,你的课程必须是 blittable !
最快的方法是UnsafeCopy8
复制 8 字节的块(32 位和 64 位)。最快的 PInvoke 方法是memcpy
.
[StructLayout(LayoutKind.Sequential)]
class MyClass
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
public int D { get; set; }
public int E { get; set; }
public int F { get; set; }
public int G { get; set; }
public byte H { get; set; }
}
class Program
{
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
static extern IntPtr memcpy(IntPtr dest, IntPtr src, UIntPtr count);
[DllImport("kernel32.dll", SetLastError = false)]
static extern void CopyMemory(IntPtr dest, IntPtr src, UIntPtr count);
static void Main()
{
MyClass mc = new MyClass { A = 1, B = 2, C = 3, D = 4, E = 5, F = 6, G = 7, H = 8 };
MyClass mc2 = new MyClass();
int size = Marshal.SizeOf(typeof(MyClass));
var gc = GCHandle.Alloc(mc, GCHandleType.Pinned);
var gc2 = GCHandle.Alloc(mc2, GCHandleType.Pinned);
IntPtr dest = gc2.AddrOfPinnedObject();
IntPtr src = gc.AddrOfPinnedObject();
// Pre-caching
memcpy(dest, src, (UIntPtr)size);
CopyMemory(dest, src, (UIntPtr)size);
UnsafeCopy(dest, src, size);
UnsafeCopy8(dest, src, size);
int cycles = 10000000;
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
var sw1 = Stopwatch.StartNew();
for (int i = 0; i < cycles; i++)
{
memcpy(dest, src, (UIntPtr)size);
}
sw1.Stop();
var sw2 = Stopwatch.StartNew();
for (int i = 0; i < cycles; i++)
{
CopyMemory(dest, src, (UIntPtr)size);
}
sw2.Stop();
var sw3 = Stopwatch.StartNew();
for (int i = 0; i < cycles; i++)
{
UnsafeCopy(dest, src, size);
}
sw3.Stop();
var sw4 = Stopwatch.StartNew();
for (int i = 0; i < cycles; i++)
{
UnsafeCopy8(dest, src, size);
}
sw4.Stop();
gc.Free();
gc2.Free();
Console.WriteLine("IntPtr.Size: {0}", IntPtr.Size);
Console.WriteLine("memcpy: {0}", sw1.ElapsedTicks);
Console.WriteLine("CopyMemory: {0}", sw2.ElapsedTicks);
Console.WriteLine("UnsafeCopy: {0}", sw3.ElapsedTicks);
Console.WriteLine("UnsafeCopy8: {0}", sw4.ElapsedTicks);
Console.ReadKey();
}
static unsafe void UnsafeCopy(IntPtr dest, IntPtr src, int size)
{
while (size >= sizeof(int))
{
*((int*)dest) = *((int*)src);
dest = (IntPtr)(((byte*)dest) + sizeof(int));
src = (IntPtr)(((byte*)src) + sizeof(int));
size -= sizeof(int);
}
if (size >= sizeof(short))
{
*((short*)dest) = *((short*)src);
dest = (IntPtr)(((byte*)dest) + sizeof(short));
src = (IntPtr)(((byte*)src) + sizeof(short));
size -= sizeof(short);
}
if (size == sizeof(byte))
{
*((byte*)dest) = *((byte*)src);
}
}
static unsafe void UnsafeCopy8(IntPtr dest, IntPtr src, int size)
{
while (size >= sizeof(long))
{
*((long*)dest) = *((long*)src);
dest = (IntPtr)(((byte*)dest) + sizeof(long));
src = (IntPtr)(((byte*)src) + sizeof(long));
size -= sizeof(long);
}
if (size >= sizeof(int))
{
*((int*)dest) = *((int*)src);
dest = (IntPtr)(((byte*)dest) + sizeof(int));
src = (IntPtr)(((byte*)src) + sizeof(int));
size -= sizeof(int);
}
if (size >= sizeof(short))
{
*((short*)dest) = *((short*)src);
dest = (IntPtr)(((byte*)dest) + sizeof(short));
src = (IntPtr)(((byte*)src) + sizeof(short));
size -= sizeof(short);
}
if (size == sizeof(byte))
{
*((byte*)dest) = *((byte*)src);
}
}
}