我的C++代码使用SSE,现在我想改进它,以便在可用时支持AVX。因此,我检测到AVX何时可用,并调用使用AVX命令的函数。我使用Win7 SP1 + VS2010 SP1和带有AVX的CPU。
要使用AVX,必须包括以下内容:
#include "immintrin.h"然后,您可以使用诸如_mm256_mul_ps、_mm256_add_ps等内置的AVX函数。问题是,默认情况下,VS2010生成的代码工作非常慢,并显示了警告:
警告C4752:找到英特尔(R)高级矢量扩展;考虑使用/arch:AVX
看起来VS2010实际上并不使用AVX指令,而是模仿它们。我将/arch:AVX添加到编译器选项中,并获得了良好的结果。但是这个选项告诉编译器在可能的情况下在任何地方都使用AVX命令。所以我的代码可能会在不支持AVX的CPU上崩溃!
因此,问题是如何使VS2010编译器产生AVX代码,但只有当我直接指定AVX本质。对于SSE,我只使用SSE函数,它生成SSE代码,而没有像/arch:SSE这样的编译器选项。但对于AVX来说,由于某些原因,它不起作用。
发布于 2011-10-20 19:35:31
2021年更新:现代版本的MSVC不需要手动使用_mm256_zeroupper(),即使是在没有/arch:AVX的情况下编译AVX的本质。VS2010做了。
您所看到的行为是昂贵的状态切换的结果。
见Agner Fog手册第102页:
http://www.agner.org/optimize/microarchitecture.pdf
每当你在SSE和AVX指令之间切换不当时,你将支付极高的(~70)循环惩罚。
在不使用/arch:AVX的情况下编译时,VS2010将生成SSE指令,但在有AVX的地方仍将使用AVX。因此,您将得到具有SSE和AVX指令的代码--这将具有状态切换惩罚。(VS2010知道这一点,所以它会发出你看到的警告。)
因此,您应该使用所有SSE或所有AVX。指定/arch:AVX告诉编译器使用所有AVX。
听起来您正在尝试创建多个代码路径:一个用于SSE,另一个用于AVX。为此,我建议您将SSE和AVX代码分成两个不同的编译单元。(一种是用/arch:AVX编译的,另一种是不编译的)然后将它们链接在一起,然后根据运行的硬件来做出调度员的选择。
如果需要来混合SSE和AVX,那么一定要适当地使用_mm256_zeroupper()或_mm256_zeroall(),以避免状态切换的惩罚。
发布于 2015-02-06 01:04:53
tl;dr仅适用于MSVC的旧版本
使用AVX对代码中的部分使用_mm256_zeroupper();或_mm256_zeroall(); (取决于函数参数)。只对带有AVX的源文件使用选项/arch:AVX,而不是对整个项目使用,以避免中断对遗留编码的SSE专用代码路径的支持。
在现代MSVC (以及其他主流编译器GCC/clang/ICC)中,编译器知道何时使用vzeroupper asm指令。强制使用额外的vzeroupper和本质,在内联时会影响性能。请参阅我需要用吗?
致
我认为最好的解释是英特尔的文章“避免AVX过渡处罚” (PDF格式)。摘要指出:
在程序中在256位Intel AVX指令和遗留Intel SSE指令之间进行转换可能会导致性能下降,因为硬件必须保存和恢复YMM寄存器的上128位。
将AVX和SSE代码分离为不同的编译单元可能无助于,如果您从启用SSE和启用AVX的对象文件中切换调用代码,因为当AVX指令或程序集与任何(来自英特尔的文件)混合时,可能会发生转换:
这意味着,在使用SSE与外部代码链接时,甚至可能会受到惩罚。
详细信息
由AVX指令定义的处理器状态有3种,其中一种状态是所有YMM寄存器都被分割,允许SSE指令使用下半部分。英特尔的文件"Intel AVX状态转换:将SSE代码迁移到AVX“提供了以下几种状态的图表:

当处于状态B (AVX-256模式)时,所有YMM寄存器的位都在使用中.当调用SSE指令时,必须发生向状态C的转换,这是存在惩罚的地方。所有YMM寄存器的上半部分必须保存到内部缓冲区中,才能启动SSE,即使它们恰好是零。转换的成本为“桑迪桥硬件上50-80个时钟周期的顺序”。还有一个来自C -> A的惩罚,如图2所示。
您还可以在第130页第9.12节“维克斯与非VEX模式之间的转换”(版本更新为2014-08-07)中找到导致这种减速的详细信息,在神秘的答案中引用。根据他的指南,任何向或从这个州的过渡都需要“桑迪桥上大约70个时钟周期”。正如英特尔的文件所述,这是一个可以避免的过渡惩罚。
Skylake有一种不同的脏-上层机制,它会对遗留系统产生错误的依赖-SSE,而不是一次性惩罚。为什么在Skylake上如果没有VZEROUPPER,这个SSE代码会慢6倍?
分辨率
为了避免过渡惩罚,您可以删除所有遗留SSE代码,指示编译器将所有SSE指令转换为128位指令的VEX编码形式(如果编译器有能力的话),或者在AVX和SSE代码之间转换之前将YMM寄存器置于已知的零状态。本质上,要维护单独的SSE代码路径,您必须在任何使用VZEROUPPER指令的代码之后,将所有16个YMM寄存器(发出指令)的上128位去掉。手动对这些位进行归零将强制转换到状态A,并且避免了代价高昂的代价,因为不需要硬件将YMM值存储在内部缓冲区中。执行此指令的内部是_mm256_zeroupper。对这一内在特性的描述是非常有用的:
当在Intel高级矢量扩展(Intel AVX)指令和遗留的Intel补充SIMD扩展(Intel SSE)指令之间转换时,这个内在特性对于清除YMM寄存器的上位非常有用。如果应用程序在英特尔高级矢量扩展(Intel)指令和遗留的英特尔补充SIMD扩展(Intel)指令之间转换之前,应用程序通过(设置为‘0’)清除所有YMM的上位,则不存在转换惩罚。
在Visual 2010+ (甚至更旧)中,你得到了内在的使用immintrin.h。
注意,用其他方法将位归零并不能消除损失--必须使用VZEROUPPER或VZEROALL指令。
英特尔编译器实现的一种自动解决方案是,如果没有任何参数是YMM寄存器或__m256/__m256d/__m256i数据类型,则在包含Intel代码的每个函数的开头处插入一个VZEROUPPER ,如果返回的值不是YMM寄存器或__m256/__m256d/__m256i数据类型,则在函数的末尾插入。
野生
FFTW使用此VZEROUPPER解决方案生成具有SSE和AVX支持的库。请参阅simd-avx.h
/* Use VZEROUPPER to avoid the penalty of switching from AVX to SSE.
See Intel Optimization Manual (April 2011, version 248966), Section
11.3 */
#define VLEAVE _mm256_zeroupper然后,在每个函数的末尾调用VLEAVE();,使用对AVX指令的本质。
https://stackoverflow.com/questions/7839925
复制相似问题