【发布时间】:2014-06-09 14:56:26
【问题描述】:
我正在尝试编写一个DynamicMethod 来包装cpblk IL 操作码。我需要在 x64 平台上复制字节数组块,这应该是最快的方法。 Array.Copy 和 Buffer.BlockCopy 都可以,但我想探索所有选项。
我的目标是将托管内存从一个字节数组复制到一个新的托管字节数组。我关心的是我如何知道如何正确“固定”内存位置。我不希望垃圾收集器移动数组并破坏一切。到目前为止它有效,但我不确定如何测试这是否是 GC 安全的。
// copying 'count' bytes from offset 'index' in 'source' to offset 0 in 'target'
// i.e. void _copy(byte[] source, int index, int count, byte[] target)
static Action<byte[], int, int, byte[]> Init()
{
var dmethod = new DynamicMethod("copy", typeof(void), new[] { typeof(object),typeof(byte[]), typeof(int), typeof(int),typeof(byte[]) },typeof(object), true);
var il = dmethod.GetILGenerator();
il.DeclareLocal(typeof(byte).MakeByRefType(), true);
il.DeclareLocal(typeof(byte).MakeByRefType(), true);
// pin the source
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Ldelema, typeof(byte));
il.Emit(OpCodes.Stloc_0);
// pin the target
il.Emit(OpCodes.Ldarg_S,(byte)4);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Ldelema, typeof(byte));
il.Emit(OpCodes.Stloc_1);
il.Emit(OpCodes.Ldloc_1);
il.Emit(OpCodes.Ldloc_0);
// load the length
il.Emit(OpCodes.Ldarg_3);
// perform the memcpy
il.Emit(OpCodes.Unaligned,(byte)1);
il.Emit(OpCodes.Cpblk);
il.Emit(OpCodes.Ret);
return dmethod.CreateDelegate(typeof(Action<byte[], int, int, byte[]>)) as Action<byte[], int, int, byte[]>;
}
【问题讨论】:
-
使用现有的方法,它们很可能与您尝试做的非常相似,并且具有能够“作弊”的优势,即使用不暴露于内部运行的代码的系统功能运行。通过尝试以自己的方式解决这个问题,您目前正在浪费时间/金钱而没有保证收益(除非它是一个研究项目,在这种情况下一定要去做)。
-
它们相似,但 il 操作码对大字节数组副本执行得更快。 (对于少于 10 个元素,它的性能非常差, Array.Copy 在这个领域似乎非常好)。最初,我引用了一个 C++/CLI dll 依赖项,我试图删除它也需要使用“不安全”编译选项。我试图将所有这些封装在一种动态方法中以避免这种烦恼。另一个优点是 IL 字节码不需要我使用原语。在这种情况下,我使用的是字节,但我也希望能够快速复制其他结构。
-
Buffer.BlockCopy 对于字节复制实际上比 array.copy 慢。它确实适用于结构类型,但仅适用于原语。例如,尝试用它复制 DateTimes,它就会爆炸。
-
OpCodes.Cpblk是 extremely slow on x86 though,视情况而定。似乎没有一般最好的算法,但其他算法似乎更稳定。不过,如果在您的用例中性能提升实际上很重要,您可以基于架构进行分支。 -
这不是我关心的问题。这适用于 x64 位环境中的服务器代码。
标签: clr cil reflection.emit