使用较新版本的SIMD版本是否可用?

当我可以使用SSE3或AVX时,可以使用SSE2或MMX等较旧的SSE版本 –
或者我还需要单独检查它们吗?

一般来说,这些都是附加的,但请记住,多年来英特尔和AMD对这些产品的支持存在差异。

如果你有AVX,那么你也可以假设SSE,SSE2,SSE3,SSSE3,SSE4.1和SSE 4.2。 请记住,要使用AVX,还需要validationOSXSAVE CPUID位是否设置为确保您使用的OS实际上也支持保存AVX寄存器。

您仍然应该明确检查您在代码中使用的所有CPUID支持的稳健性(例如同时检查AVX,OSXSAVE,SSE4,SSE3,SSSE3以保护您的AVX代码路径)。

#include  inline bool IsAVXSupported() { #if defined(_M_IX86 ) || defined(_M_X64) int CPUInfo[4] = {-1}; __cpuid( CPUInfo, 0 ); if ( CPUInfo[0] < 1 ) return false; __cpuid(CPUInfo, 1 ); int ecx = 0x10000000 // AVX | 0x8000000 // OSXSAVE | 0x100000 // SSE 4.2 | 0x80000 // SSE 4.1 | 0x200 // SSSE3 | 0x1; // SSE3 if ( ( CPUInfo[2] & ecx ) != ecx ) return false; return true; #else return false; #endif } 

所有能够支持x64本机的处理器都需要SSE和SSE2,因此它们是所有代码的良好基线假设。 即使对于x86架构,Windows 8.0,Windows 8.1和Windows 10也明确要求SSE和SSE2支持,因此这些指令集非常普遍。 换句话说,如果您检查SSE或SSE2失败,只需退出应用程序并发生致命错误。

 #include  inline bool IsSSESupported() { #if defined(_M_IX86 ) || defined(_M_X64) return ( IsProcessorFeaturePresent( PF_XMMI_INSTRUCTIONS_AVAILABLE ) != 0 && IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ) != 0 ); #else return false; #endif } 

-要么-

 #include  inline bool IsSSESupported() { #if defined(_M_IX86 ) || defined(_M_X64) int CPUInfo[4] = {-1}; __cpuid( CPUInfo, 0 ); if ( CPUInfo[0] < 1 ) return false; __cpuid(CPUInfo, 1 ); int edx = 0x4000000 // SSE2 | 0x2000000; // SSE if ( ( CPUInfo[3] & edx ) != edx ) return false; return true; #else return false; #endif } 

另外,请记住MMX,x87 FPU和AMD 3DNow! *是x64本机的所有已弃用的指令集,因此您不应再在新代码中主动使用它们。 一个好的经验法则是避免使用任何返回__m64或采用__m64数据类型的内在函数。

您可能需要查看此DirectXMath博客系列,其中包含许多这些指令集的注释以及相关的处理器支持要求。

(*) - 所有AMD 3DNow! 除了PREFETCHPREFETCHW之外,不推荐使用这些指令。 第一代Intel64处理器缺乏对这些指令的支持,但后来被添加,因为它们被认为是核心X64指令集的一部分。 Windows 8.1和Windows 10 x64特别需要PREFETCHW ,尽管测试有点奇怪。 Broadwell之前的大多数英特尔CPU实际上并不通过CPUID报告对PREFETCHW支持,但是它们将操作码视为无操作,而不是抛出“非法指令”exception。 因此,这里的测试是(a)它是由CPUID支持的,(b)如果没有, PREFETCHW至少不会抛出exception。

下面是Visual Studio的一些测试代码,它演示了PREFETCHW测试以及x86和x64平台的许多其他CPUID位。

 #include  #include  #include  #include  void main() { unsigned int x = _mm_getcsr(); printf("%08X\n", x ); bool prefetchw = false; // See http://msdn.microsoft.com/en-us/library/hskdteyh.aspx int CPUInfo[4] = {-1}; __cpuid( CPUInfo, 0 ); if ( CPUInfo[0] > 0 ) { __cpuid(CPUInfo, 1 ); // EAX { int stepping = (CPUInfo[0] & 0xf); int basemodel = (CPUInfo[0] >> 4) & 0xf; int basefamily = (CPUInfo[0] >> 8) & 0xf; int xmodel = (CPUInfo[0] >> 16) & 0xf; int xfamily = (CPUInfo[0] >> 20) & 0xff; int family = basefamily + xfamily; int model = (xmodel << 4) | basemodel; printf("Family %02X, Model %02X, Stepping %u\n", family, model, stepping ); } // ECX if ( CPUInfo[2] & 0x20000000 ) // bit 29 printf("F16C\n"); if ( CPUInfo[2] & 0x10000000 ) // bit 28 printf("AVX\n"); if ( CPUInfo[2] & 0x8000000 ) // bit 27 printf("OSXSAVE\n"); if ( CPUInfo[2] & 0x400000 ) // bit 22 printf("MOVBE\n"); if ( CPUInfo[2] & 0x100000 ) // bit 20 printf("SSE4.2\n"); if ( CPUInfo[2] & 0x80000 ) // bit 19 printf("SSE4.1\n"); if ( CPUInfo[2] & 0x2000 ) // bit 13 printf("CMPXCHANG16B\n"); if ( CPUInfo[2] & 0x1000 ) // bit 12 printf("FMA3\n"); if ( CPUInfo[2] & 0x200 ) // bit 9 printf("SSSE3\n"); if ( CPUInfo[2] & 0x1 ) // bit 0 printf("SSE3\n"); // EDX if ( CPUInfo[3] & 0x4000000 ) // bit 26 printf("SSE2\n"); if ( CPUInfo[3] & 0x2000000 ) // bit 25 printf("SSE\n"); if ( CPUInfo[3] & 0x800000 ) // bit 23 printf("MMX\n"); } else printf("CPU doesn't support Feature Identifiers\n"); if ( CPUInfo[0] >= 7 ) { __cpuidex(CPUInfo, 7, 0); // EBX if ( CPUInfo[1] & 0x100 ) // bit 8 printf("BMI2\n"); if ( CPUInfo[1] & 0x20 ) // bit 5 printf("AVX2\n"); if ( CPUInfo[1] & 0x8 ) // bit 3 printf("BMI\n"); } else printf("CPU doesn't support Structured Extended Feature Flags\n"); // Extended features __cpuid( CPUInfo, 0x80000000 ); if ( CPUInfo[0] > 0x80000000 ) { __cpuid(CPUInfo, 0x80000001 ); // ECX if ( CPUInfo[2] & 0x10000 ) // bit 16 printf("FMA4\n"); if ( CPUInfo[2] & 0x800 ) // bit 11 printf("XOP\n"); if ( CPUInfo[2] & 0x100 ) // bit 8 { printf("PREFETCHW\n"); prefetchw = true; } if ( CPUInfo[2] & 0x80 ) // bit 7 printf("Misalign SSE\n"); if ( CPUInfo[2] & 0x40 ) // bit 6 printf("SSE4A\n"); if ( CPUInfo[2] & 0x1 ) // bit 0 printf("LAHF/SAHF\n"); // EDX if ( CPUInfo[3] & 0x80000000 ) // bit 31 printf("3DNow!\n"); if ( CPUInfo[3] & 0x40000000 ) // bit 30 printf("3DNowExt!\n"); if ( CPUInfo[3] & 0x20000000 ) // bit 29 printf("x64\n"); if ( CPUInfo[3] & 0x100000 ) // bit 20 printf("NX\n"); } else printf("CPU doesn't support Extended Feature Identifiers\n"); if ( !prefetchw ) { bool illegal = false; __try { static const unsigned int s_data = 0xabcd0123; _m_prefetchw(&s_data); } __except (EXCEPTION_EXECUTE_HANDLER) { illegal = true; } if (illegal) { printf("PREFETCHW is an invalid instruction on this processor\n"); } } } 

更新:当然,根本的挑战是如何处理缺乏AVX支持的系统? 虽然指令集很有用,但拥有支持AVX的处理器的最大好处是能够使用/arch:AVX构建开关,该开关可以全局使用VEX前缀以获得更好的SSE / SSE2代码。 唯一的问题是生成的代码DLL / EXE与缺乏AVX支持的系统不兼容。

因此,对于Windows,理想情况下,您应该为非AVX系统构建一个EXE(假设SSE / SSE2仅使用/arch:SSE2而不是x86代码;此设置对x64代码是隐含的),一个针对x4代码优化的不同EXE AVX(使用/arch:AVX ),然后使用CPU检测来确定给定系统使用哪个EXE。

幸运的是,使用Xbox One,我们可以随时使用/arch::AVX构建,因为它是一个固定的平台......

作为一般规则 – 除非必须,否则不要混合使用不同代的SSE / AVX。 如果这样做,请确保使用vzeroupper或类似的状态清除指令,否则您可能会拖动部分值并在不知不觉中创建错误的依赖关系,因为大多数寄存器在模式之间共享即使清除,在模式之间切换也可能会导致处罚,具体取决于确切的微架构。

进一步阅读 – https://software.intel.com/sites/default/files/m/d/4/1/d/8/11MC12_Avoiding_2BAVX-SSE_2BTransition_2BPenalties_2Brh_2Bfinal.pdf

查看Chuck的答案,就你应该做什么提出好的建议。 如果您感到好奇,请查看此答案以获得所提问题的字面答案。


AVX支持绝对保证支持所有英特尔 SSE *指令集,因为它包含所有这些指令集的VEX编码版本。 正如Chuck指出的那样,您可以使用位掩码同时检查以前的代码,而不会使代码膨胀,但不要冒汗。

请注意,POPCNT,TZCNT和类似的东西不是SSE的任何东西。 POPCNT有自己的function位。 LZCNT也有自己的function位,因为AMD将它与BMI1分开引入。 不过,TZCNT只是BMI1的一部分。 由于一些BMI1指令使用VEX编码,即使是最新一代的Pentium / Celeron CPU(如Skylake Pentium)也没有BMI1。 :(我认为英特尔只是想省略AVX / AVX2,可能因此他们可以将具有错误的执行单元上层通道的CPU作为Pentiums出售,并且他们通过禁用解码器中的VEX支持来实现这一点。


到目前为止,所有发布的CPU都支持英特尔SSE支持。 SSE4.1意味着SSSE3,SSE3,SSE2和SSE。 SSE4.2意味着所有前述内容。 我不确定是否有任何官方x86文档排除了具有SSE4.1支持但不支持SSSE3的CPU的可能性。 (即遗漏PSHUFB,这可能是昂贵的实施。)但实际上这种可能性极小,因为这会违反许多人的假设。 正如我所说,它甚至可能被官方禁止,但我没有仔细检查。


AVX不包括AMD SSE4a或AMD XOP。 必须特别检查AMD扩展。 另请注意,最新的AMD CPU正在降低XOP支持。 (英特尔从来没有采用它,所以大多数人都没有编写代码来利用它,所以对于AMD来说,那些晶体管大多是浪费的。它确实有一些很好的东西,比如2源字节置换,允许一个字节LUT两次宽度为PSHUFB,没有AVX2的VPSHUFB ymm的通道限制。


SSE2是x86-64架构的基线 。 您不必在64位版本中检查SSE或SSE2支持。 我忘了MMX是否也是基线。 几乎可以确定。

SSE指令集包括一些在MMX寄存器上运行的指令。 (例如, PMAXSW mm1, mm2/m64是SSE的新产品PMAXSW mm1, mm2/m64版本是SSE2的一部分。)即使支持SSE的32位CPU也需要具有MMX寄存器。 拥有MMX寄存器但仅支持使用它们的SSE指令,而不是原始的MMX指令(例如movq mm0, [mem] )会很疯狂。 但是,我还没有找到任何明确规定基于x86的Deathstation 9000与SSE但不是MMX CPUIDfunction位的可能性,但我没有涉及到英特尔的官方x86手册。 (有关链接,请参阅x86标记wiki)。

不管怎样,不要使用MMX,即使在XMM寄存器的低半部分中一次只有64位工作,它通常也会更慢。 最新的CPU(如Intel Skylake)的某些指令的MMX版本的吞吐量低于XMM版本。 在某些情况下,延迟甚至更差。 例如,根据Agner Fog的测试 ,在Skylake上, PACKSSWB mm0, mm1PACKSSWB mm0, mm1为3 PACKSSWB mm0, mm1延迟为2c。 128b和256b XMM / YMM版本为1 uop,延迟为1c。