如何检查 CPU 是否支持 SSE3指令集?

下面的代码对于检查 CPU 是否支持 SSE3指令集是否有效?

使用 IsProcessorFeaturePresent()函数显然不能在 WindowsXP 上工作。

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;
}
52533 次浏览

我已经创建了一个 GitHub repro,它将检测所有主要 x86ISA 扩展的 CPU 和 OS 支持: 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 的回答了解如何检测此寄存器。
  • 使用(AVX512)512位 zmm和掩码寄存器的说明。检测操作系统对 AVX512的支持与 AVX 相同,但使用的标志是 0xe6而不是 0x6

神秘博士的回答有点危险——它解释了如何检测 CPU 支持而不是操作系统支持。您需要使用 _ xgetbv 来检查操作系统是否启用了所需的 CPU 扩展状态。请参阅 给你了解另一个来源。就连 GCC 也犯了同样的错误。代码的主要内容是:

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;
}

经过大量的谷歌搜索,我还找到了来自英特尔的解决方案:

链接: 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"))
// ...

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

在 Mac 操作系统上可以这样工作:

sysctl -a | grep machdep.cpu.features

在我的机器上,它输出如下信息:

特点: FPU VME DE PSE TSC MSR PAE MCE CX8 APIC SEP VERR 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 POVNT AES PCID XSAVE OSXSAVE SEGLIM64 TSCTMR AVX1.0 RDRAND F16C

正如您可以看到用粗体写的指令,SSE3和一堆其他 SIMD 指令是受支持的。

阿比鲁普的回答还有: 在 linux 上,您可以运行这个 shell 命令来找出您的 CPU 所支持的特性

cat /proc/cpuinfo | grep flags | uniq

在我的机器上打印

标志: fpu vme de se tsc msr pae mce cx8 apic sep mtrr pge mca cmov patse36 clrush mmx fxsr sse sse2 ht syscall nx pdpe1gb rdtscp lm Constant _ tsc rep _ good nopl x 拓扑 nonstop _ tsc perfmperf egerfpu pniPclmulqdq ssse3 fma cx16 pcid sse4 _ 1 sse4 _ 2 x2apic move be popcnt tsc _ date _ timer as xsave avx f16c rdrand hypervisor lahf _ lm abm 3dnowprefettinvpcid _ single retpoline kaiser fsgsbase bmi1 hle avx2 smp bmi2 erms invccid rtm rdds adx xsaveopt

在 linux 或 wsl2上,使用 Util-linux存储库中的 lscpu命令完成这项工作。

例如:

lscpu | grep sse3

或者“ POSIX 方式”:

cat /proc/cpuinfo |grep -i sse3 >/dev/null 2>&1 && echo "ESS3=TRUE" || echo "ESS3=FALSE"

结果:

ESS3=TRUE