zoukankan      html  css  js  c++  java
  • [VC] CPUIDFIELD:CPUID字段的统一编号、读取方案。范例:检查SSE4A、AES、PCLMULQDQ指令

    除了基本的MMX和SSE系列指令集外,x86体系还有其他扩展指令集,例如SSE4A、AES、PCLMULQDQ等,它们也可以利用CPUID指令来检测。但是,这些指令集细碎杂多。如果像以前那样分别编写检测函数的话,那工作量太大,不值得。而且大量的函数名也会给使用带来麻烦。于是文篇探讨如何设计一套通用的检测方案。


    零、指令简介

      SSE4A指令:是AMD提出的,最早出现在2007年的K10微架构的处理器上。它针对Intel的SSE4指令集修改而来,去除其中对I64优化的指令,保留图形、影音编码、3D运算、游戏等多媒体指令,并完全兼容。
      AES指令:是Intel提出的,最早出现在2010年的Westmere微架构的 Core i7/i5 处理器上。能提高AES(Advanced Encryption Standard,高级加密标准)加解密性能。
      PCLMULQDQ指令:是Intel提出的,最早出现在2010年的Westmere微架构处理器上。它是不进位乘法(Carryless multiply)运算,主要用于加解密处理。

      检查以下CPUID标志位可判断硬件是否支持——
    CPUID(80000001).ECX.SSE4A[bit 6]=1 // 硬件支持SSE4A
    CPUID(1).ECX.AES[bit 25]=1 // 硬件支持AES
    CPUID(1).ECX.PCLMULQDQ[bit 1]=1 // 硬件支持PCLMULQDQ

      SSE4A、AES、PCLMULQDQ是基于XMM寄存器的,所以在使用前还应该检查操作系统是否支持SSE指令集。
      对于支持AVX指令集的处理器(2011年的Sandy Bridge微架构),AES、PCLMULQDQ也能使用YMM寄存器。同理,在使用YMM寄存器前,应检查操作系统是否支持AVX指令集。


    一、基本思路

      对于这种情况,最常见的处理办法是定义一个检测函数和一堆检测类型常数。
      例如可以将检测类型常数顺序编号——

    #define CHECKTYPE_SSE4A    0
    #define CHECKTYPE_AES    1
    #define CHECKTYPE_PCLMULQDQ    2
    ...
    BOOL simd_check(int checktype)
    {
        switch(checktype)
        {
            case CHECKTYPE_SSE4A:
                检测SSE4A
                break;
            case CHECKTYPE_AES:
                检测AES
                break;
            case CHECKTYPE_PCLMULQDQ:
                检测PCLMULQDQ
                break;
        }
        return FALSE;
    }


      例如检测硬件是否支持SSE4A,就调用“simd_check(CHECKTYPE_SSE4A)”。
      在实际使用这些指令时,还应该检测操作系统是否支持SSE,即这样做——

    if (simd_sse_level(NULL)>0)
    {
        if (simd_check(CHECKTYPE_SSE4A))
        {
            使用SSE4A指令
        }
    }


      有了上述函数后,使用起来的确是方便了一些。但是该方案存在以下缺陷——
    1.编码量大。检测类型常数没有规律性,对于每一种检测类型,都得在switch的case分支中写一段监测代码。这些代码很相似,只是使用的常数不同。
    2.扩充不易。万一以后Intel或AMD又增加基于XMM/YMM的新指令,那么又需要增加常数、修改simd_check函数。
    3.功能单一。除了SIMD类指令的位标识外,CPUID还有很多丰富的信息,比如基于通用寄存器的运算指令(CRC32、POPCNT等)、系统标识等字段。对于这么多东西,如果分别编写不同的检测函数、定义好几套常数的话,那么不仅代码量大,而且用起来不方便。


    二、CPUIDFIELD编号方案

      CPUID字段数据类型大致可分为4类——
    1.位。如是否支持某种指令(MMX、SSE1/2/3/3S/4.1/4.2/4A……),是否具有某种功能(PSE、PAE、APIC……)等。
    2.整数。如处理器型号的Model/Family/BrandId信息,物理地址长度等。
    3.字符串。如厂商、商标字符串。
    4.其他。如CPUID的功能2获取缓存描述。

      第1类是最常见的,第2类也很多,而第3类、第4类就只有寥寥几种。
      于是我想,有没有办法将第1类和第2类信息进行统一编号。这样就可以用一个函数获取CPUID的绝大多数信息了。

      观察CPUID文档,发现定位一个字段需要这些参数——
    1.功能号:即CPUID指令的EAX参数。常见范围是0~0Dh、80000000h~8000001Eh。(如AES是1)
    2.子功能号:即CPUID指令的ECX参数。大多数时候为0,目前的最大值是62(功能0Dh)。(如AES是0)
    3.寄存器:即CPUID指令返回的寄存器。是EAX、EBX、ECX、EDX这4个32位寄存器中的某一个。(如AES是ECX)
    4.位偏移:该字段的最低位是32位寄存器中的哪一位。范围是0~31。(如AES是25)
    5.位长:该字段的位长。对于位标识来说,位长总是1(如AES)。而对于整数型(如处理器型号的Model/Family/BrandId信息),范围是2~32。

      使用一个32位整数来对它们编号——
    typedef INT32 CPUIDFIELD;

      分析一下上述参数需要多少位——
    1.功能号:理论上需要32位。
    2.子功能号:理论上需要32位。但现在一般在0~62的范围内,即6位。
    3.寄存器:4个寄存器,需要2位。
    4.位偏移:0~31,需要5位。
    5.位长:1~32,需要5位(+1编码。如0代表1,31代表32)。

      第3、4、5参数的位数已经确定,共2+5+5=12位。
      对于第2个参数,虽然目前只用到6位,但考虑到十六进制的书写问题与未来发展,定为8位较好。(注:书写十六进制时,一个字符是4位,8位是两个4位)。
      现在还剩下12位,可以对第1个参数进行编码。可以将高4位映射到功能号的高4位,以区分标准功能与扩展功能。然后再将低8位映射到功能号的低8位,以支持各个功能。
      具体的编码方案为——
    bits 31:28:功能号的高4位(bits 31:28)。
    bits 27:20:功能号的低8位(bits 7:0)。
    bits 19:12:子功能号的低8位(bits 7:0)。
    bits 11:10:寄存器编号。0=EAX, 1=EBX, 2=ECX, 3=EDX。
    bits 9:5:位长(+1编码)。
    bits 4:0:位偏移。将位偏移放在最低位是为了十六进制的可读性。因为很多字段的位长为1,编码为0,这时看十六进制的最低2个字符就知道位偏移是多少。

      在C语言中定义它们的掩码与位移量——

    #define  CPUIDFIELD_MASK_POS    0x0000001F    // 位偏移. 0~31.
    #define  CPUIDFIELD_MASK_LEN    0x000003E0    // 位长. 1~32
    #define  CPUIDFIELD_MASK_REG    0x00000C00    // 寄存器. 0=EAX, 1=EBX, 2=ECX, 3=EDX.
    #define  CPUIDFIELD_MASK_FIDSUB    0x000FF000    // 子功能号(低8位).
    #define  CPUIDFIELD_MASK_FID    0xFFF00000    // 功能号(最高4位 和 低8位).
    
    #define CPUIDFIELD_SHIFT_POS    0
    #define CPUIDFIELD_SHIFT_LEN    5
    #define CPUIDFIELD_SHIFT_REG    10
    #define CPUIDFIELD_SHIFT_FIDSUB    12
    #define CPUIDFIELD_SHIFT_FID    20


      然后再编写一些宏,用于参数的组成与拆解——

    #define CPUIDFIELD_MAKE(fid,fidsub,reg,pos,len)    (((fid)&0xF0000000) \
        | ((fid)<<CPUIDFIELD_SHIFT_FID & 0x0FF00000) \
        | ((fidsub)<<CPUIDFIELD_SHIFT_FIDSUB & CPUIDFIELD_MASK_FIDSUB) \
        | ((reg)<<CPUIDFIELD_SHIFT_REG & CPUIDFIELD_MASK_REG) \
        | ((pos)<<CPUIDFIELD_SHIFT_POS & CPUIDFIELD_MASK_POS) \
        | (((len)-1)<<CPUIDFIELD_SHIFT_LEN & CPUIDFIELD_MASK_LEN) \
        )
    #define CPUIDFIELD_FID(cpuidfield)    ( ((cpuidfield)&0xF0000000) | (((cpuidfield) & 0x0FF00000)>>CPUIDFIELD_SHIFT_FID) )
    #define CPUIDFIELD_FIDSUB(cpuidfield)    ( ((cpuidfield) & CPUIDFIELD_MASK_FIDSUB)>>CPUIDFIELD_SHIFT_FIDSUB )
    #define CPUIDFIELD_REG(cpuidfield)    ( ((cpuidfield) & CPUIDFIELD_MASK_REG)>>CPUIDFIELD_SHIFT_REG )
    #define CPUIDFIELD_POS(cpuidfield)    ( ((cpuidfield) & CPUIDFIELD_MASK_POS)>>CPUIDFIELD_SHIFT_POS )
    #define CPUIDFIELD_LEN(cpuidfield)    ( (((cpuidfield) & CPUIDFIELD_MASK_LEN)>>CPUIDFIELD_SHIFT_LEN) + 1 )

      为了检查这些宏是否正常工作,在main函数中编写一些测试代码——

        //CPUIDFIELD cpuf = CPUIDFIELD_MAKE(0x8000000D,62,0,0,32);
        //printf("0x%.8X\n", cpuf);
        //printf("fid:\t0x%X\n", CPUIDFIELD_FID(cpuf));
        //printf("fidsub:\t0x%X\n", CPUIDFIELD_FIDSUB(cpuf));
        //printf("reg:\t0x%X\n", CPUIDFIELD_REG(cpuf));
        //printf("pos:\t0x%X\n", CPUIDFIELD_POS(cpuf));
        //printf("len:\t0x%X\n", CPUIDFIELD_LEN(cpuf));

      现在可以为SSE4A、AES、PCLMULQDQ定义常数了——

    #define CPUF_SSE4A    CPUIDFIELD_MAKE(0x80000001,0,2,6,1)
    #define CPUF_AES    CPUIDFIELD_MAKE(1,0,2,25,1)
    #define CPUF_PCLMULQDQ    CPUIDFIELD_MAKE(1,0,2,1,1)



    三、读取函数

      有了CPUIDFIELD编号方案后,读取函数就很容易编写了。
      虽然可以将代码全部写在一个函数中。但是为了提高代码的可读性和可复用性,将它分成2个函数与1个宏会更好——

    // 取得位域
    #ifndef __GETBITS32
    #define __GETBITS32(src,pos,len)    ( ((src)>>(pos)) & (((UINT32)-1)>>(32-len)) )
    #endif
    
    // 根据CPUIDFIELD从缓冲区中获取字段.
    UINT32    getcpuidfield_buf(const INT32 dwBuf[4], CPUIDFIELD cpuf)
    {
        return __GETBITS32(dwBuf[CPUIDFIELD_REG(cpuf)], CPUIDFIELD_POS(cpuf), CPUIDFIELD_LEN(cpuf));
    }
    
    // 根据CPUIDFIELD获取CPUID字段.
    UINT32    getcpuidfield(CPUIDFIELD cpuf)
    {
        INT32 dwBuf[4];
        __cpuidex(dwBuf, CPUIDFIELD_FID(cpuf), CPUIDFIELD_FIDSUB(cpuf));
        return getcpuidfield_buf(dwBuf, cpuf);
    }


      说明——
    __GETBITS32:专门用于提取位域。它是常用的位运算操作,为了避免重复定义,用宏比较好。
    getcpuidfield:标准的获取CPUID字段函数。用法很简单,只需传递一个CPUIDFIELD参数就行了。
    getcpuidfield_buf:有时候需要一次获得多个CPUID字段,并且已经知道它们属于同一套功能号。这时为了提高效率,可以先用__cpuidex获得那4个寄存器的信息,然后分别调用getcpuidfield_buf。

      范例——

        printf("SSE4A: %d\n", getcpuidfield(CPUF_SSE4A));
        printf("AES: %d\n", getcpuidfield(CPUF_AES));
        printf("PCLMULQDQ: %d\n", getcpuidfield(CPUF_PCLMULQDQ));



    四、全部代码

      全部代码——

    #include <windows.h>
    #include <stdio.h>
    #include <conio.h>
    #include <tchar.h>
    
    #if _MSC_VER >=1400    // VC2005才支持intrin.h
    #include <intrin.h>    // 所有Intrinsics函数
    #else
    #include <emmintrin.h>    // MMX, SSE, SSE2
    #endif
    
    
    // CPUIDFIELD
    typedef INT32 CPUIDFIELD;
    
    #define  CPUIDFIELD_MASK_POS    0x0000001F    // 位偏移. 0~31.
    #define  CPUIDFIELD_MASK_LEN    0x000003E0    // 位长. 1~32
    #define  CPUIDFIELD_MASK_REG    0x00000C00    // 寄存器. 0=EAX, 1=EBX, 2=ECX, 3=EDX.
    #define  CPUIDFIELD_MASK_FIDSUB    0x000FF000    // 子功能号(低8位).
    #define  CPUIDFIELD_MASK_FID    0xFFF00000    // 功能号(最高4位 和 低8位).
    
    #define CPUIDFIELD_SHIFT_POS    0
    #define CPUIDFIELD_SHIFT_LEN    5
    #define CPUIDFIELD_SHIFT_REG    10
    #define CPUIDFIELD_SHIFT_FIDSUB    12
    #define CPUIDFIELD_SHIFT_FID    20
    
    #define CPUIDFIELD_MAKE(fid,fidsub,reg,pos,len)    (((fid)&0xF0000000) \
        | ((fid)<<CPUIDFIELD_SHIFT_FID & 0x0FF00000) \
        | ((fidsub)<<CPUIDFIELD_SHIFT_FIDSUB & CPUIDFIELD_MASK_FIDSUB) \
        | ((reg)<<CPUIDFIELD_SHIFT_REG & CPUIDFIELD_MASK_REG) \
        | ((pos)<<CPUIDFIELD_SHIFT_POS & CPUIDFIELD_MASK_POS) \
        | (((len)-1)<<CPUIDFIELD_SHIFT_LEN & CPUIDFIELD_MASK_LEN) \
        )
    #define CPUIDFIELD_FID(cpuidfield)    ( ((cpuidfield)&0xF0000000) | (((cpuidfield) & 0x0FF00000)>>CPUIDFIELD_SHIFT_FID) )
    #define CPUIDFIELD_FIDSUB(cpuidfield)    ( ((cpuidfield) & CPUIDFIELD_MASK_FIDSUB)>>CPUIDFIELD_SHIFT_FIDSUB )
    #define CPUIDFIELD_REG(cpuidfield)    ( ((cpuidfield) & CPUIDFIELD_MASK_REG)>>CPUIDFIELD_SHIFT_REG )
    #define CPUIDFIELD_POS(cpuidfield)    ( ((cpuidfield) & CPUIDFIELD_MASK_POS)>>CPUIDFIELD_SHIFT_POS )
    #define CPUIDFIELD_LEN(cpuidfield)    ( (((cpuidfield) & CPUIDFIELD_MASK_LEN)>>CPUIDFIELD_SHIFT_LEN) + 1 )
    
    // 取得位域
    #ifndef __GETBITS32
    #define __GETBITS32(src,pos,len)    ( ((src)>>(pos)) & (((UINT32)-1)>>(32-len)) )
    #endif
    
    
    #define CPUF_SSE4A    CPUIDFIELD_MAKE(0x80000001,0,2,6,1)
    #define CPUF_AES    CPUIDFIELD_MAKE(1,0,2,25,1)
    #define CPUF_PCLMULQDQ    CPUIDFIELD_MAKE(1,0,2,1,1)
    
    
    // SSE系列指令集的支持级别. simd_sse_level 函数的返回值。
    #define SIMD_SSE_NONE    0    // 不支持
    #define SIMD_SSE_1    1    // SSE
    #define SIMD_SSE_2    2    // SSE2
    #define SIMD_SSE_3    3    // SSE3
    #define SIMD_SSE_3S    4    // SSSE3
    #define SIMD_SSE_41    5    // SSE4.1
    #define SIMD_SSE_42    6    // SSE4.2
    
    const char*    simd_sse_names[] = {
        "None",
        "SSE",
        "SSE2",
        "SSE3",
        "SSSE3",
        "SSE4.1",
        "SSE4.2",
    };
    
    
    
    
    char szBuf[64];
    INT32 dwBuf[4];
    
    #if defined(_WIN64)
    // 64位下不支持内联汇编. 应使用__cpuid、__cpuidex等Intrinsics函数。
    #else
    #if _MSC_VER < 1600    // VS2010. 据说VC2008 SP1之后才支持__cpuidex
    void __cpuidex(INT32 CPUInfo[4], INT32 InfoType, INT32 ECXValue)
    {
        if (NULL==CPUInfo)    return;
        _asm{
            // load. 读取参数到寄存器
            mov edi, CPUInfo;    // 准备用edi寻址CPUInfo
            mov eax, InfoType;
            mov ecx, ECXValue;
            // CPUID
            cpuid;
            // save. 将寄存器保存到CPUInfo
            mov    [edi], eax;
            mov    [edi+4], ebx;
            mov    [edi+8], ecx;
            mov    [edi+12], edx;
        }
    }
    #endif    // #if _MSC_VER < 1600    // VS2010. 据说VC2008 SP1之后才支持__cpuidex
    
    #if _MSC_VER < 1400    // VC2005才支持__cpuid
    void __cpuid(INT32 CPUInfo[4], INT32 InfoType)
    {
        __cpuidex(CPUInfo, InfoType, 0);
    }
    #endif    // #if _MSC_VER < 1400    // VC2005才支持__cpuid
    
    #endif    // #if defined(_WIN64)
    
    // 根据CPUIDFIELD从缓冲区中获取字段.
    UINT32    getcpuidfield_buf(const INT32 dwBuf[4], CPUIDFIELD cpuf)
    {
        return __GETBITS32(dwBuf[CPUIDFIELD_REG(cpuf)], CPUIDFIELD_POS(cpuf), CPUIDFIELD_LEN(cpuf));
    }
    
    // 根据CPUIDFIELD获取CPUID字段.
    UINT32    getcpuidfield(CPUIDFIELD cpuf)
    {
        INT32 dwBuf[4];
        __cpuidex(dwBuf, CPUIDFIELD_FID(cpuf), CPUIDFIELD_FIDSUB(cpuf));
        return getcpuidfield_buf(dwBuf, cpuf);
    }
    
    // 取得CPU厂商(Vendor)
    //
    // result: 成功时返回字符串的长度(一般为12)。失败时返回0。
    // pvendor: 接收厂商信息的字符串缓冲区。至少为13字节。
    int cpu_getvendor(char* pvendor)
    {
        INT32 dwBuf[4];
        if (NULL==pvendor)    return 0;
        // Function 0: Vendor-ID and Largest Standard Function
        __cpuid(dwBuf, 0);
        // save. 保存到pvendor
        *(INT32*)&pvendor[0] = dwBuf[1];    // ebx: 前四个字符
        *(INT32*)&pvendor[4] = dwBuf[3];    // edx: 中间四个字符
        *(INT32*)&pvendor[8] = dwBuf[2];    // ecx: 最后四个字符
        pvendor[12] = '\0';
        return 12;
    }
    
    // 取得CPU商标(Brand)
    //
    // result: 成功时返回字符串的长度(一般为48)。失败时返回0。
    // pbrand: 接收商标信息的字符串缓冲区。至少为49字节。
    int cpu_getbrand(char* pbrand)
    {
        INT32 dwBuf[4];
        if (NULL==pbrand)    return 0;
        // Function 0x80000000: Largest Extended Function Number
        __cpuid(dwBuf, 0x80000000);
        if (dwBuf[0] < 0x80000004)    return 0;
        // Function 80000002h,80000003h,80000004h: Processor Brand String
        __cpuid((INT32*)&pbrand[0], 0x80000002);    // 前16个字符
        __cpuid((INT32*)&pbrand[16], 0x80000003);    // 中间16个字符
        __cpuid((INT32*)&pbrand[32], 0x80000004);    // 最后16个字符
        pbrand[48] = '\0';
        return 48;
    }
    
    
    // 是否支持MMX指令集
    BOOL    simd_mmx(BOOL* phwmmx)
    {
        const INT32    BIT_D_MMX = 0x00800000;    // bit 23
        BOOL    rt = FALSE;    // result
        INT32 dwBuf[4];
    
        // check processor support
        __cpuid(dwBuf, 1);    // Function 1: Feature Information
        if ( dwBuf[3] & BIT_D_MMX )    rt=TRUE;
        if (NULL!=phwmmx)    *phwmmx=rt;
    
        // check OS support
        if ( rt )
        {
    #if defined(_WIN64)
            // VC编译器不支持64位下的MMX。
            rt=FALSE;
    #else
            __try 
            {
                _mm_empty();    // MMX instruction: emms
            }
            __except (EXCEPTION_EXECUTE_HANDLER)
            {
                rt=FALSE;
            }
    #endif    // #if defined(_WIN64)
        }
    
        return rt;
    }
    
    // 检测SSE系列指令集的支持级别
    int    simd_sse_level(int* phwsse)
    {
        const INT32    BIT_D_SSE = 0x02000000;    // bit 25
        const INT32    BIT_D_SSE2 = 0x04000000;    // bit 26
        const INT32    BIT_C_SSE3 = 0x00000001;    // bit 0
        const INT32    BIT_C_SSSE3 = 0x00000100;    // bit 9
        const INT32    BIT_C_SSE41 = 0x00080000;    // bit 19
        const INT32    BIT_C_SSE42 = 0x00100000;    // bit 20
        int    rt = SIMD_SSE_NONE;    // result
        INT32 dwBuf[4];
    
        // check processor support
        __cpuid(dwBuf, 1);    // Function 1: Feature Information
        if ( dwBuf[3] & BIT_D_SSE )
        {
            rt = SIMD_SSE_1;
            if ( dwBuf[3] & BIT_D_SSE2 )
            {
                rt = SIMD_SSE_2;
                if ( dwBuf[2] & BIT_C_SSE3 )
                {
                    rt = SIMD_SSE_3;
                    if ( dwBuf[2] & BIT_C_SSSE3 )
                    {
                        rt = SIMD_SSE_3S;
                        if ( dwBuf[2] & BIT_C_SSE41 )
                        {
                            rt = SIMD_SSE_41;
                            if ( dwBuf[2] & BIT_C_SSE42 )
                            {
                                rt = SIMD_SSE_42;
                            }
                        }
                    }
                }
            }
        }
        if (NULL!=phwsse)    *phwsse=rt;
    
        // check OS support
        __try 
        {
            __m128 xmm1 = _mm_setzero_ps();    // SSE instruction: xorps
            if (0!=*(int*)&xmm1)    rt = SIMD_SSE_NONE;    // 避免Release模式编译优化时剔除上一条语句
        }
        __except (EXCEPTION_EXECUTE_HANDLER)
        {
            rt = SIMD_SSE_NONE;
        }
    
        return rt;
    }
    
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        //__cpuidex(dwBuf, 0,0);
        //__cpuid(dwBuf, 0);
        //printf("%.8X\t%.8X\t%.8X\t%.8X\n", dwBuf[0],dwBuf[1],dwBuf[2],dwBuf[3]);
    
        cpu_getvendor(szBuf);
        printf("CPU Vendor:\t%s\n", szBuf);
    
        cpu_getbrand(szBuf);
        printf("CPU Name:\t%s\n", szBuf);
    
        BOOL bhwmmx;    // 硬件支持MMX.
        BOOL bmmx;    // 操作系统支持MMX.
        bmmx = simd_mmx(&bhwmmx);
        printf("MMX: %d\t// hw: %d\n", bmmx, bhwmmx);
    
        int    nhwsse;    // 硬件支持SSE.
        int    nsse;    // 操作系统支持SSE.
        nsse = simd_sse_level(&nhwsse);
        printf("SSE: %d\t// hw: %d\n", nsse, nhwsse);
        for(int i=1; i<sizeof(simd_sse_names); ++i)
        {
            if (nhwsse>=i)    printf("\t%s\n", simd_sse_names[i]);
        }
    
        // test CPUIDFIELD
        //CPUIDFIELD cpuf = CPUIDFIELD_MAKE(0x8000000D,62,0,0,32);
        //printf("0x%.8X\n", cpuf);
        //printf("fid:\t0x%X\n", CPUIDFIELD_FID(cpuf));
        //printf("fidsub:\t0x%X\n", CPUIDFIELD_FIDSUB(cpuf));
        //printf("reg:\t0x%X\n", CPUIDFIELD_REG(cpuf));
        //printf("pos:\t0x%X\n", CPUIDFIELD_POS(cpuf));
        //printf("len:\t0x%X\n", CPUIDFIELD_LEN(cpuf));
    
        // test SSE4A/AES/PCLMULQDQ
        printf("SSE4A: %d\n", getcpuidfield(CPUF_SSE4A));
        printf("AES: %d\n", getcpuidfield(CPUF_AES));
        printf("PCLMULQDQ: %d\n", getcpuidfield(CPUF_PCLMULQDQ));
    
        return 0;
    }

      在以下编译器中成功编译——
    VC6(32位)
    VC2003(32位)
    VC2005(32位)
    VC2010(32位、64位)


    五、测试结果

      在64位的win7中运行“x64\Release\getcpuidfield_2010.exe”,运行效果——

      利用cmdarg_ui运行“Debug\getcpuidfield.exe”,顺便测试WinXP与VC6——

    参考文献——
    《Intel® 64 and IA-32 Architectures Software Developer’s Manual Combined Volumes:1, 2A, 2B, 2C, 3A, 3B, and 3C》. May 2012. http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html
    《Intel® Processor Identification and the CPUID Instruction》. April 2012. http://developer.intel.com/content/www/us/en/processors/processor-identification-cpuid-instruction-note.html
    《AMD64 Architecture Programmer's Manual Volume 3: General Purpose and System Instructions》. December 2011. http://support.amd.com/us/Processor_TechDocs/24594_APM_v3.pdf
    《AMD CPUID Specification》. September 2010. http://support.amd.com/us/Embedded_TechDocs/25481.pdf
    《x86 architecture CPUID》. http://www.sandpile.org/x86/cpuid.htm
    《[x86]SIMD指令集发展历程表(MMX、SSE、AVX等)》. http://www.cnblogs.com/zyl910/archive/2012/02/26/x86_simd_table.html
    《如何在各个版本的VC及64位下使用CPUID指令》. http://www.cnblogs.com/zyl910/archive/2012/05/21/vcgetcpuid.html
    《[VC兼容32位和64位] 检查MMX和SSE系列指令集的支持级别》. http://www.cnblogs.com/zyl910/archive/2012/05/25/checksimd64.html
    《[C#] cmdarg_ui:“简单参数命令行程序”的通用图形界面》.  http://www.cnblogs.com/zyl910/archive/2012/06/19/cmdarg_ui.html


    源码下载——
    https://files.cnblogs.com/zyl910/getcpuidfield.rar

  • 相关阅读:
    Java数组的使用
    Java的栈堆以及数组两种不同类型的定义
    Java数组声明的创建
    JAVA递归
    Java可变参数
    Java方法(类--------对象--------方法)
    html块元素和内联元素的区别
    HTML基础介绍
    CSS网页美化元素属性介绍
    ArrayList类的remove(Object o)方法简述
  • 原文地址:https://www.cnblogs.com/zyl910/p/getcpuidfield.html
Copyright © 2011-2022 走看看