避免AVX-SSE(VEX)过渡处罚

时间:2017-05-09 21:11:29

标签: avx sse vex

我们的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字节内存的收益?

5 个答案:

答案 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编码指令的部分之后发出vzeroallvzeroupper,然后再返回使用非代码的其余代码-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日的回答未提及。

http://www.agner.org/optimize/blog/read.php?i=761

的Agnger博客上也有一些关于VZEROUPPER的有趣信息