c00*_*0fd 4 c++ x86 assembly compiler-optimization visual-c++
我正在检查使用最新版本的 VS 2017 C++ 编译器完成的项目的发布版本。我很好奇为什么编译器选择构建以下代码片段:
//ncbSzBuffDataUsed of type INT32
UINT8* pDst = (UINT8*)(pMXB + 1);
UINT8* pSrc = (UINT8*)pDPE;
for(size_t i = 0; i < (size_t)ncbSzBuffDataUsed; i++)
{
pDst[i] = pSrc[i];
}
Run Code Online (Sandbox Code Playgroud)
像这样:
UINT8* pDst = (UINT8*)(pMXB + 1);
UINT8* pSrc = (UINT8*)pDPE;
for(size_t i = 0; i < (size_t)ncbSzBuffDataUsed; i++)
00007FF66441251E 4C 63 C2 movsxd r8,edx
00007FF664412521 4C 2B D1 sub r10,rcx
00007FF664412524 0F 1F 40 00 nop dword ptr [rax]
00007FF664412528 0F 1F 84 00 00 00 00 00 nop dword ptr [rax+rax]
00007FF664412530 41 0F B6 04 0A movzx eax,byte ptr [r10+rcx]
{
pDst[i] = pSrc[i];
00007FF664412535 88 01 mov byte ptr [rcx],al
00007FF664412537 48 8D 49 01 lea rcx,[rcx+1]
00007FF66441253B 49 83 E8 01 sub r8,1
00007FF66441253F 75 EF jne _logDebugPrint_in_MainXchgBuffer+0A0h (07FF664412530h)
}
Run Code Online (Sandbox Code Playgroud)
与仅使用一条REP MOVSB
指令相比?后者不是效率更高吗?
编辑:首先,rep movsb
彼得·科德斯告诉我们,这里有一个内在的速度会快得多,我相信他(我想我已经相信了)。如果您想强制编译器以这种方式执行操作,请参阅: https __movsb()
: //learn.microsoft.com/en-us/cpp/intrinsics/movsb。
至于为什么编译器没有为您执行此操作,在没有任何其他想法的情况下,答案可能是寄存器压力。要使用rep movsb
编译器必须:
rsi
(=源地址)rdi
(=目标地址)rcx
(=计数)rep movsb
因此,现在它必须用完rep movsb
指令规定的三个寄存器,并且它可能宁愿不这样做。具体而言rsi
, 和rdi
预计会在函数调用中保留,因此,如果编译器可以在任何特定函数的主体中使用它们,它就会(至少在首次进入该方法时)rcx
保留this
指针。
此外,通过我们看到编译器在那里生成的代码,r10
和rcx
寄存器可能已经包含必需的源地址和目标地址(我们在您的示例中看不到),如果是这样,这对编译器来说将很方便。
在实践中,您可能会看到编译器在不同情况下做出不同的选择。请求的优化类型(/O1
- 优化大小,vs /O2
- 优化速度)也可能会影响这一点。
更多关于 x64 寄存器传递约定的信息请参见此处,更多关于 x64 ABI 的信息请参见此处。
编辑2(再次受到Peter评论的启发):
编译器可能决定不对循环进行矢量化,因为它不知道指针是否对齐或可能重叠。如果没有看到更多代码,我们无法确定。但这与我的回答并不严格相关,考虑到OP实际询问的内容。