我们的64位应用程序有很多代码(特别是在标准库中),它们在SSE模式下使用xmm0-xmm7寄存器。
我想使用ymm寄存器实现快速内存复制。我无法修改使用xmm寄存器添加VEX前缀的所有代码,我也认为这是不实际的,因为它会增加代码的大小,因为需要CPU解码更大的指令会使它运行得更慢
我只想使用两个ymm寄存器(可能zmm - 支持zmm的经济实惠的处理器可以在今年推出)用于快速内存复制。
问题是:如何使用ymm寄存器但避免过渡处罚?
当我只使用ymm8-ymm15寄存器(不是ymm0-ymm7)时会发生惩罚吗? SSE最初有8个128位寄存器(xmm0-xmm7),但在64位模式下,(xmm8-xmm15)也可用于非VEX前缀指令。但是,我已经审查了我们的64位应用程序,它只使用xmm0-xmm7,因为它也有一个32位版本,几乎相同的代码。仅当CPU尝试使用之前使用过的xmm寄存器为ymm并且具有高128位非零值时才会发生惩罚吗?将快速内存复制后使用的ymm寄存器归零是不是更好?例如,我曾使用ymm寄存器复制32个字节的内存 - 将它归零的最快方法是什么?是" vpxor ymm15,ymm15,ymm15"够快吗? (AFAIK,vpxor可以在3个ALU执行端口中的任何一个上执行,p0 / p1 / p5,而vxorpd只能在p5上执行)。是不是将它归零的时间超过使用它来复制32字节内存的收益?
答案 0 :(得分:5)
最佳解决方案可能是使用VEX前缀重新编译所有代码。 VEX编码指令的大小与相同指令的非VEX版本大小相同,因为非VEX指令带有许多前缀和转义码的遗留(由于指令中有短视补丁的长期历史)编码方案)。 VEX前缀将所有旧前缀和转义码组合成一个前缀为2或3个字节(AVX512为4个字节)。
VEX /非VEX转换在不同处理器上以不同方式工作(请参阅Why is this SSE code 6 times slower without VZEROUPPER on Skylake?):
较旧的英特尔处理器:处理器中不同内部状态之间的干净转换需要VZEROUPPER指令。
在英特尔Skylake或更高版本的处理器上:需要使用VZEROUPPER以避免非VEX指令错误地依赖于寄存器的上半部分。
在当前的AMD处理器上:256位寄存器被视为两个128位寄存器。除了与英特尔处理器的兼容性外,不需要VZEROUPPER。 VZEROUPPER的成本约为6个时钟周期。
在所有说明中使用VEX前缀的优点是可以避免所有处理器上的这些转换成本。您的遗留代码可能会受益于热内部循环中的某些256位操作。
VEX前缀的缺点是代码与旧处理器不兼容,因此您可能需要保留旧版本以便在旧处理器上运行
答案 1 :(得分:5)
另一种可能性是使用寄存器zmm16-zmm31。这些调节器没有非VEX对应物。将zmm16-zmm31与非VEX SSE代码混合不会产生状态转换,也不会造成任何损失。这些512位寄存器仅在64位模式下可用,并且仅在具有AVX512的处理器上可用。
答案 2 :(得分:4)
为避免对所有体系结构进行处罚,只需在代码中使用VEX编码指令的部分之后发出vzeroall
或vzeroupper
,然后再返回使用非代码的其余代码-VEX指令。
无论如何,发布这些指令被认为是所有AVX使用例程的好习惯,并且很便宜 - 除了可能在Knights Landing上,但我怀疑你是否在使用该架构。即使你是,性能特征也与桌面/ Xeon系列完全不同,所以你可能还想在那里单独编译。
这些仅指令从脏上部状态移动到干净上部状态。您不能简单地将您使用的特定寄存器归零,因为芯片不会逐个寄存器地跟踪脏状态。
这些vzero*
指令的成本是几个周期:因此,如果您在AVX中所做的任何事情都值得,那么支付这笔小额费用通常是值得的。
答案 3 :(得分:2)
根据我的经验,Avoiding AVX-SSE (VEX) Transition Penalties
的最佳方法是让编译器使用微架构的本机代码。例如,您可以在SSE-Intrinsics
旁边使用AVX-Intrinsics
并使用-march=native
。我的GCC 6.2
编译程序并使用VEX-Encoded
指令。如果您看到生成的程序集,您将在所有SSE翻译代码之前找到额外的v
。另一方面,如果您有疑问,可以在使用__asm__ __volatile__ ( "vzeroupper" : : : );
寄存器之后使用ymm
程序的每个点,但是您应该小心。
答案 4 :(得分:0)
我在https://software.intel.com/en-us/forums/intel-isa-extensions/topic/704023
的英特尔论坛上找到了Agner感兴趣的说明它回答了如果我在应用程序使用xmm0-xmm7时使用ymm8-ymm9会发生什么情况的问题,所以我们使用不同的寄存器。
这是引用。
我刚刚在Haswell上做了几个实验。它对待所有的矢量 如果只有一个ymm寄存器,则注册为具有脏的上半部分 被感动了。换句话说,如果你修改ymm1然后修改非VEX 写入xmm2的指令将具有错误的依赖关系 先前的xmm2值。骑士登陆没有这种虚假的依赖。 也许是分别记住每个寄存器的状态?
希望未来的英特尔处理器能够记住它的状态 每个寄存器分开,或至少分别处理zmm16-zmm31 他们没有污染xmm0-xmm15。你能透露些什么吗? 此?
2016年12月28日的回答未提及。
的Agnger博客上也有一些关于VZEROUPPER的有趣信息