cpblk
的{{3}}有点稀疏:
cpblk
指令从源地址(类型unsigned int32
,*
或native int
)复制一个数字(类型&
)到目标地址(类型*
,native int
或&
)。如果源和目标区域重叠,则cpblk
的行为未指定。
cpblk
假设所寻址的源和目标都与机器的自然大小对齐。cpblk
指令可以紧跟在unaligned.
指令之前,以指示源或目标是未对齐的。
好的,与Array.Copy
,Marshal.Copy
和Buffer.BlockCopy
等其他批量复制操作相比,我们知道:
这给我留下了一些问题:
native int
,"非托管指针"是否重要?或"托管指针(&
)"?Buffer.BlockCopy
仅适用于基本类型,而不是结构,即使它们只包含基本类型)根据The MSDN documentation钉扎是不必要的,但支持说明是错误的。 (我怀疑大型物体堆不会被压缩,这是一种过度概括)
ECMA-335也不是很有帮助。那里的指令描述包含相同的措辞并添加
[基本原理:
cpblk
用于复制结构(而不是任意字节运行)。由CLI分配的所有这些结构自然地与当前平台对齐。因此,生成cpblk
指令的编译器不需要知道代码最终是在32位还是64位平台上执行。 结束理由]
好的,这听起来应该接受比Buffer.BlockCopy
更多的类型。但仍然不是任意类型。
也许新发布的.NET核心源代码会有一些答案。
答案 0 :(得分:8)
cpblk
及其随附的initblk
直接映射到任何本地语言编译器依赖于初始化和复制结构的内在函数。无需等待.NETCore源,您可以从SSCLI20,clr / src / fjit / fjitdef.h中看到它们的语义。一个简单的抖动,它将cpblk
直接转换为memcpy()
,initblk
到memset()
的调用。与C编译器使用的内在函数相同。
当然不考虑GC,C#和VB.NET编译器根本不使用这些操作码。但是C ++ / CLI编译器就是一个简单的例子:
using namespace System;
struct s { int a; int b; };
int main(array<System::String ^> ^args)
{
s var = {}; // initblk
s cpy = var; // cpblk
return 0;
}
优化的MSIL:
.method assembly static int32 main(string[] args) cil managed
{
// Code size 34 (0x22)
.maxstack 3
.locals ([0] valuetype s cpy,
[1] valuetype s var)
IL_0000: ldloca.s var
IL_0002: ldc.i4.0
IL_0003: ldc.i4.8
IL_0004: initblk
IL_0006: ldloca.s cpy
IL_0008: ldloca.s var
IL_000a: ldc.i4.8
IL_000b: cpblk
...
}
当前的.NET抖动生成内联代码,其中包含针对小型结构的简单寄存器移动,针对大型结构的REP STOS / MOVS。与Buffer.Memcpy()的作用非常相似。