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

Max*_*tin 4 x86 assembly sse avx micro-optimization

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

A F*_*Fog 8

最佳解决方案可能是使用VEX前缀重新编译所有代码.VEX编码指令的大小与相同指令的非VEX版本大小相同,因为非VEX指令带有许多前缀和转义码的遗留(由于指令中有短视补丁的长期历史)编码方案).VEX前缀将所有旧前缀和转义码组合成两个或三个字节的单个前缀(AVX512为四个字节).

VEX /非VEX转换在不同的处理器上以不同的方式工作(请参阅为什么这个SSE代码在Skylake上没有VZEROUPPER时要慢6倍?):

较旧的Intel处理器:处理器中不同内部状态之间的干净转换需要VZEROUPPER指令.

在英特尔Skylake或更高版本的处理器上:需要使用VZEROUPPER以避免非VEX指令错误地依赖于寄存器的上部.

在当前的AMD处理器上:256位寄存器被视为两个128位寄存器.除了与英特尔处理器的兼容性外,不需要VZEROUPPER.VZEROUPPER的成本约为6个时钟周期.

在所有指令上使用VEX前缀的优点是可以避免所有处理器上的这些转换成本.您的遗留代码可能会受益于热内部循环中的某些256位操作.

VEX前缀的缺点是代码与旧处理器不兼容,因此您可能需要保留旧版本以便在旧处理器上运行


A F*_*Fog 8

另一种可能性是使用寄存器zmm16-zmm31。这些调节器没有非VEX对应物。将zmm16-zmm31与非VEX SSE代码混合不会产生状态转换,也不会造成任何损失。这些512位寄存器仅在64位模式下可用,并且仅在具有AVX512的处理器上可用。


Bee*_*ope 7

为了避免对所有体系结构的惩罚,在返回使用非VEX指令的其余代码之前,只需要在使用VEX编码指令的代码部分vzeroallvzeroupper之后发布该代码即可。

无论如何,发出这些指令对于所有使用AVX的例程来说都是很好的做法,而且很便宜-也许在Knights Landing上除外,但是我怀疑您正在使用该体系结构。即使您是,性能特征也与台式机/ Xeon系列完全不同,因此无论如何您可能都希望在其中进行单独的编译。

这些是从脏鞋面转移到干净鞋面状态的唯一说明。您不能简单地将使用过的特定寄存器归零,因为该芯片不会逐个寄存器地跟踪脏状态。

这些vzero*指令的成本是几个周期:因此,如果您在AVX中所做的任何事情都值得,那么付出这么小的成本通常是值得的。


Mar*_*tin 5

以我的经验,最好的方法Avoiding AVX-SSE (VEX) Transition Penalties是让编译器使用微体系结构的本机代码。例如,您可以SSE-IntrinsicsAVX-Intrinsics和旁边使用-march=native。我GCC 6.2编译程序并使用VEX-Encoded指令。如果您看到生成的程序集,您将v在所有SSE转换的代码之前找到额外的内容。另一方面,如果您不确定可以__asm__ __volatile__ ( "vzeroupper" : : : );在使用ymm寄存器之后使用程序的每个点,但是您应该注意这一点。


归档时间:

查看次数:

908 次

最近记录:

6 年,9 月 前