【问题标题】:How to check if a CPU supports the SSE3 instruction set?如何检查 CPU 是否支持 SSE3 指令集?
【发布时间】:2011-09-01 13:59:07
【问题描述】:

下面的代码是否可以用来检查 CPU 是否支持 SSE3 指令集?

使用IsProcessorFeaturePresent() 函数显然不适用于 Windows XP。

bool CheckSSE3()
{
    int CPUInfo[4] = {-1};

    //-- Get number of valid info ids
    __cpuid(CPUInfo, 0);
    int nIds = CPUInfo[0];

    //-- Get info for id "1"
    if (nIds >= 1)
    {
        __cpuid(CPUInfo, 1);
        bool bSSE3NewInstructions = (CPUInfo[2] & 0x1) || false;
        return bSSE3NewInstructions;     
    }

    return false;      
}

【问题讨论】:

  • 这似乎是正确的,据我阅读英特尔® 64 和 IA-32 架构软件开发人员手册第 2 卷(2A 和 2B):指令集参考,AZ,第 284 页。此外, CPUInfo[2] 的第 9 位表示补充 SSE3 指令。
  • SSE3AVX(以及 CLMULMOVD)是不同的功能,它们分别进行测试。来自 Intel 手册(由 Norbert 引用),第 3-189 页:“软件必须在使用该功能之前使用 CPUID 返回的功能标志确认处理器功能是否存在。软件不应依赖于保留所有功能的未来产品." 所以不要仅仅因为SSE3 存在就依赖AVX 的可用性。
  • 另外,CPU 支持不同于操作系统支持。请参阅下面安迪的回答。
  • bool bSSE3NewInstructions = (CPUInfo[2] & 0x1) || false; 上,你不需要|| false 部分:bool bSSE3NewInstructions = (CPUInfo[2] & 0x1); 然后你也可以去掉bSSE3NewInstructions 变量:return (CPUInfo[2] & 0x1);

标签: c++ sse instruction-set avx cpuid


【解决方案1】:

Linux 或 wsl2 上的 Alternativley 来自 util-linux 存储库的 lscpucommand 将完成这项工作。

例如:

lscpu | grep sse3

【讨论】:

    【解决方案2】:

    在 Mac OS 上可以使用:

    sysctl -a | grep machdep.cpu.features
    

    在我的机器上它输出这个:

    machdep.cpu.features:FPU VME DE PSE TSC MSR PAE MCE CX8 APIC SEP MTRR PGE MCA CMOV PAT PSE36 CLFSH DS ACPI MMX FXSR SSE SSE2 SS HTT TM PBE SSE3强> PCLMULQDQ DTES64 MON DSCPL VMX EST TM2 SSSE3 FMA CX16 TPR PDCM SSE4.1 SSE4.2 x2APIC MOVBE POPCNT AES PCID XSAVE OSXSAVE SEGLIM64 TSCTMR AVX1.0 RDRAND F16C

    如您所见,以粗体字书写的指令支持 SSE3 和一堆其他 SIMD 指令。

    【讨论】:

    • 你忘了加粗 SSSE3,其中包括像 pshufb 这样的重要内容。您还遗漏了 FMA,这对于某些用途非常重要。 (奇怪的是你有 FMA 但没有 AVX2。那是你 Mac 中的 AMD Piledriver 或 Steamroller CPU 吗?)
    • 由于某种原因,AVX2 支持列在machdep.cpu.leaf7_features 而不是machdep.cpu.features
    【解决方案3】:

    经过一番谷歌搜索,我还找到了英特尔的解决方案:

    链接:https://software.intel.com/en-us/articles/how-to-detect-new-instruction-support-in-the-4th-generation-intel-core-processor-family

        void cpuid(uint32_t eax, uint32_t ecx, uint32_t* abcd) {
    #if defined(_MSC_VER)
                __cpuidex((int*)abcd, eax, ecx);
    #else
                uint32_t ebx, edx;
    # if defined( __i386__ ) && defined ( __PIC__ )
                /* in case of PIC under 32-bit EBX cannot be clobbered */
                __asm__("movl %%ebx, %%edi \n\t cpuid \n\t xchgl %%ebx, %%edi" : "=D" (ebx),
    # else
                __asm__("cpuid" : "+b" (ebx),
    # endif
                "+a" (eax), "+c" (ecx), "=d" (edx));
                abcd[0] = eax; abcd[1] = ebx; abcd[2] = ecx; abcd[3] = edx;
    #endif
        }
    
        int check_xcr0_ymm()
        {
            uint32_t xcr0;
    #if defined(_MSC_VER)
            xcr0 = (uint32_t)_xgetbv(0);  /* min VS2010 SP1 compiler is required */
    #else
            __asm__("xgetbv" : "=a" (xcr0) : "c" (0) : "%edx");
    #endif
            return ((xcr0 & 6) == 6); /* checking if xmm and ymm state are enabled in XCR0 */
        }
    

    另请注意,GCC 有一些您可以使用的特殊内在函数(请参阅:https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/X86-Built-in-Functions.html):

        if (__builtin_cpu_supports("avx2"))
        // ...
    

    如果你把这些和上面的信息放在一起,一切都会好起来的。

    【讨论】:

    • 现代 GNU C 编译器知道如何围绕 PIC 代码中的 inline-asm 语句保存/恢复 EBX。不再需要此解决方法,但除了可能在整个程序中运行几次的函数中可能会产生一点点代码大小开销之外,不会造成损害。 (缓存您的 CPUID 结果;这不是一条快速指令。)
    【解决方案4】:

    添加到 Abhiroop 的答案: 在 linux 上,你可以运行这个 shell 命令来找出你的 CPU 支持的特性

    cat /proc/cpuinfo | grep flags | uniq

    在我的机器上打印

    标志:FPU VME DE PSE TSC MSR PAE MCE CX8 APIC SEP MTRR PGE MCA CMOV PAT PSE36 CLFLUSH MMX FXSR SSE SSE2 HT SSE NX PDPE1GB RDTSCP LM Constant_TSC REB_GOOD NOPL XTopology NORSTOP_TSC APERFMPERF EAGERFPU PNI PCLMULQDQ SSSE3 FMA CX16 PCID SSE4_1 SSE4_2 X2APIC MOVBE popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single retpoline kaiser fsgsbase bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx xsaveopt

    【讨论】:

      【解决方案5】:

      我创建了一个 GitHub repro,它将检测所有主要 x86 ISA 扩展的 CPU 和操作系统支持:https://github.com/Mysticial/FeatureDetector

      这是一个较短的版本:


      首先需要访问CPUID指令:

      #ifdef _WIN32
      
      //  Windows
      #define cpuid(info, x)    __cpuidex(info, x, 0)
      
      #else
      
      //  GCC Intrinsics
      #include <cpuid.h>
      void cpuid(int info[4], int InfoType){
          __cpuid_count(InfoType, 0, info[0], info[1], info[2], info[3]);
      }
      
      #endif
      

      然后就可以运行以下代码了:

      //  Misc.
      bool HW_MMX;
      bool HW_x64;
      bool HW_ABM;      // Advanced Bit Manipulation
      bool HW_RDRAND;
      bool HW_BMI1;
      bool HW_BMI2;
      bool HW_ADX;
      bool HW_PREFETCHWT1;
      
      //  SIMD: 128-bit
      bool HW_SSE;
      bool HW_SSE2;
      bool HW_SSE3;
      bool HW_SSSE3;
      bool HW_SSE41;
      bool HW_SSE42;
      bool HW_SSE4a;
      bool HW_AES;
      bool HW_SHA;
      
      //  SIMD: 256-bit
      bool HW_AVX;
      bool HW_XOP;
      bool HW_FMA3;
      bool HW_FMA4;
      bool HW_AVX2;
      
      //  SIMD: 512-bit
      bool HW_AVX512F;    //  AVX512 Foundation
      bool HW_AVX512CD;   //  AVX512 Conflict Detection
      bool HW_AVX512PF;   //  AVX512 Prefetch
      bool HW_AVX512ER;   //  AVX512 Exponential + Reciprocal
      bool HW_AVX512VL;   //  AVX512 Vector Length Extensions
      bool HW_AVX512BW;   //  AVX512 Byte + Word
      bool HW_AVX512DQ;   //  AVX512 Doubleword + Quadword
      bool HW_AVX512IFMA; //  AVX512 Integer 52-bit Fused Multiply-Add
      bool HW_AVX512VBMI; //  AVX512 Vector Byte Manipulation Instructions
      
      int info[4];
      cpuid(info, 0);
      int nIds = info[0];
      
      cpuid(info, 0x80000000);
      unsigned nExIds = info[0];
      
      //  Detect Features
      if (nIds >= 0x00000001){
          cpuid(info,0x00000001);
          HW_MMX    = (info[3] & ((int)1 << 23)) != 0;
          HW_SSE    = (info[3] & ((int)1 << 25)) != 0;
          HW_SSE2   = (info[3] & ((int)1 << 26)) != 0;
          HW_SSE3   = (info[2] & ((int)1 <<  0)) != 0;
      
          HW_SSSE3  = (info[2] & ((int)1 <<  9)) != 0;
          HW_SSE41  = (info[2] & ((int)1 << 19)) != 0;
          HW_SSE42  = (info[2] & ((int)1 << 20)) != 0;
          HW_AES    = (info[2] & ((int)1 << 25)) != 0;
      
          HW_AVX    = (info[2] & ((int)1 << 28)) != 0;
          HW_FMA3   = (info[2] & ((int)1 << 12)) != 0;
      
          HW_RDRAND = (info[2] & ((int)1 << 30)) != 0;
      }
      if (nIds >= 0x00000007){
          cpuid(info,0x00000007);
          HW_AVX2   = (info[1] & ((int)1 <<  5)) != 0;
      
          HW_BMI1        = (info[1] & ((int)1 <<  3)) != 0;
          HW_BMI2        = (info[1] & ((int)1 <<  8)) != 0;
          HW_ADX         = (info[1] & ((int)1 << 19)) != 0;
          HW_SHA         = (info[1] & ((int)1 << 29)) != 0;
          HW_PREFETCHWT1 = (info[2] & ((int)1 <<  0)) != 0;
      
          HW_AVX512F     = (info[1] & ((int)1 << 16)) != 0;
          HW_AVX512CD    = (info[1] & ((int)1 << 28)) != 0;
          HW_AVX512PF    = (info[1] & ((int)1 << 26)) != 0;
          HW_AVX512ER    = (info[1] & ((int)1 << 27)) != 0;
          HW_AVX512VL    = (info[1] & ((int)1 << 31)) != 0;
          HW_AVX512BW    = (info[1] & ((int)1 << 30)) != 0;
          HW_AVX512DQ    = (info[1] & ((int)1 << 17)) != 0;
          HW_AVX512IFMA  = (info[1] & ((int)1 << 21)) != 0;
          HW_AVX512VBMI  = (info[2] & ((int)1 <<  1)) != 0;
      }
      if (nExIds >= 0x80000001){
          cpuid(info,0x80000001);
          HW_x64   = (info[3] & ((int)1 << 29)) != 0;
          HW_ABM   = (info[2] & ((int)1 <<  5)) != 0;
          HW_SSE4a = (info[2] & ((int)1 <<  6)) != 0;
          HW_FMA4  = (info[2] & ((int)1 << 16)) != 0;
          HW_XOP   = (info[2] & ((int)1 << 11)) != 0;
      }
      

      请注意,这只检测 CPU 是否支持指令。要实际运行它们,您还需要有操作系统支持。

      具体来说,需要操作系统支持:

      • x64 指令。 (您需要 64 位操作系统。)
      • 使用 (AVX) 256 位 ymm 寄存器的指令。请参阅 Andy Lutomirski's answer 了解如何检测。
      • 使用 (AVX512) 512 位 zmm 和掩码寄存器的指令。检测操作系统对 AVX512 的支持与 AVX 相同,但使用标志 0xe6 而不是 0x6

      【讨论】:

      • 对像我这样的其他人的注意事项:仔细阅读问题 - __cpuid 内在函数仅适用于 MSVC。
      • @slugchewer 好点。在 GCC 中,我相信你需要使用内联汇编。让我看看我是否能找到一个已经存在的解决方案。
      • @slugchewer 我添加了一个内联汇编版本,它应该适用于 GCC、ICC 和可能的 Clang。我还没有测试过。如果它坏了,请告诉我。
      • 这个答案不正确。您充其量只是在检查 CPU 是否支持 AVX、XOP 等。您无法使用 xgetbv 检查操作系统是否启用了所需的 CPU 状态。如果您在带有旧操作系统的新 CPU 上运行代码,您的代码将会崩溃。
      • @jww 我可能会把它改成this instead。然后我把问题转嫁给 GCC 的人。 :P (他们已经解决了哪些问题?)因为我们使用的是 GCC 特定的内联汇编,所以不应该有任何损失可移植性。 ICC 支持 Windows 上的 MSVC 内部函数,我相信 Linux 上的 GCC 内置函数(见鬼,它们甚至支持 Windows 上的 GCC 内联 asm)。所以应该问题不大。不过我不确定 Clang。
      【解决方案6】:

      Mysticial 的回答有点危险——它解释了如何检测 CPU 支持而不是操作系统支持。您需要使用 _xgetbv 来检查操作系统是否启用了所需的 CPU 扩展状态。有关其他来源,请参阅 hereEven gcc has made the same mistake.代码的肉是:

      bool avxSupported = false;
      
      int cpuInfo[4];
      __cpuid(cpuInfo, 1);
      
      bool osUsesXSAVE_XRSTORE = cpuInfo[2] & (1 << 27) || false;
      bool cpuAVXSuport = cpuInfo[2] & (1 << 28) || false;
      
      if (osUsesXSAVE_XRSTORE && cpuAVXSuport)
      {
          unsigned long long xcrFeatureMask = _xgetbv(_XCR_XFEATURE_ENABLED_MASK);
          avxSupported = (xcrFeatureMask & 0x6) == 0x6;
      }
      

      【讨论】:

      • +1,所以我不必自己查找和测试。我将保持我的回答具体到 CPU 是否支持它,并指出你的关于 256 位 AVX 的正确操作系统支持。
      • 关于_xgetbv:使用MSVC,你需要#include &lt;immintrin.h&gt;,使用GCC你需要#include &lt;xsaveintrin.h&gt;。同样在 GCC 中,_XCR_XFEATURE_ENABLED_MASK 不存在,因此您应该只使用_xgetbv(0)。感谢您的代码:)
      猜你喜欢
      • 1970-01-01
      • 2021-08-31
      • 1970-01-01
      • 2013-01-31
      • 1970-01-01
      • 2011-10-17
      • 2011-07-11
      • 2020-09-12
      • 2012-10-08
      相关资源
      最近更新 更多