使用AVX指令会禁用exp()优化吗?

7
我正在使用AVX指令集在VC++中编写前馈网络,并通过C#中的PInvoke调用此代码。当调用计算包括函数exp()的大循环的函数时,循环大小为160M,我的性能约为1000ms。但是,一旦调用了任何使用AVX指令集的函数,然后随后使用exp(),我的性能会降至相同操作的大约8000ms左右。请注意,计算exp()的函数是标准C,而使用AVX指令集的调用可以在数据被处理方面完全不相关。某种标志在运行时被触发。换句话说,
A(); // 1000ms calculates 160M exp() 
B(); // completely unrelated but contains AVX
A(); // 8000ms

或者,奇怪的是,
C(); // contains 128 bit SSE SIMD expressions
A(); // 1000ms

我对这里可能发生的机制或如何解决问题感到困惑。我的电脑是Intel 2500K CPU和Windows 7操作系统,我使用的是VS Express版本。

谢谢。

1个回答

10
如果使用任何AVX256指令,则“AVX上部状态”将变为“脏状态”,这会导致大量停顿,如果随后使用SSE指令(包括在xmm寄存器中执行的标量浮点),则会出现此问题。这在Intel Optimization Manual中有记录,您可以免费下载(如果您正在进行此类工作,则必须阅读):
AVX指令始终修改YMM寄存器的上半部分,并且SSE指令不修改上半部分。从硬件角度来看,YMM寄存器集合的上半部分可以视为处于以下三种状态之一: • 清洁:所有YMM的上半部分都为零。这是处理器从RESET开始时的状态。 • 修改并保存到XSAVE区域:YMM寄存器的上半部分内容与XSAVE区域中保存的数据匹配。在XSAVE / XRSTOR执行后发生这种情况。 • 修改但未保存:执行一个AVX指令(256位或128位)会修改目标YMM的上半部分。
无论何时处理器状态为“修改但未保存”,都会应用AVX / SSE转换惩罚。使用VZEROUPPER将处理器状态移动到“清洁”状态,避免转换惩罚。
您的例程B()会使YMM状态变脏,因此A()中的SSE代码会停顿。在B和A之间插入一个VZEROUPPER指令以避免此问题。

我会说它真的奏效了。这意味着exp()正在使用128位SSE而不是256位代码。我不太熟悉,不知道是否可以方便地转换。 - AronMiller
1
@AronMiller:很高兴能帮到你。确保在使用AVX并将控制权传递给你不拥有的代码时,使用VZEROUPPER。同时,请务必向编译器提交错误报告,以尝试让它在这些情况下自动插入该指令。 - Stephen Canon
有从SSE到AVX128的直接翻译,记录在指令参考手册中。我认为ICC可以为您进行转换,但我不知道其他编译器是否已经这样做了。 - Stephen Canon
谢谢提供文档参考。 - AronMiller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接