從2011年的Sandy Bridge微架構處理器開始,現在支持AVX系列指令集的處理器越來越多了。本文探討如何用VC編寫檢測AVX系列指令集的程序,並利用了先前的CPUIDFIELD方案。
一、AVX系列指令集簡介
SSE5 指令:SSE5 是一個紙面上的指令集,並沒有最終實現,AMD 在 2007 年 8 月公布 SSE5 指令集規范,在 2009 年 5 月 AMD 推出了 XOP,FMA4 以及 CVT16 來取代 SSE5 指令。
AVX 指令:2008 年 3 月 Intel 發布了 AVX(Advanced Vector Extensions)指令集規范,首次在 Sandy Bridge 微架構的處理器上使用。AMD 首次在 Bulldozer 微架構的處理器上加入 AVX 指令的支持。
FMA 指令:FMA 指令是 AVX 指令集中的一部分,Intel 將在 2013 年的 Haswell 微架構處理器上使用。據說AMD將在2012年的Piledriver微架構處理器上支持FMA。
XOP,FMA4 以及 CVT16 指令:AMD 在 2009 年 5 月發布了 XOP,FMA4 以及 CVT16 指令集規范,這些指令集取代了 SSE5 指令,在原有的 SSE5 指令基礎上,使用了兼容 AVX 指令的設計方案重新進行了設計,因此,XOP,FMA4 以及 CVT16 在指令的編碼方面是兼容於 AVX 的方案。這使得 AVX/FAM4/CVT16 指令與 AVX 指令同時存在,而不會產生沖突。AMD首次在 Bulldozer 微架構的處理器上使用。
F16C 指令:F16C指令就是AMD的CVT16指令,Intel換了一個名稱,隨后AMD也接收了這一稱呼。Intel 首次在 2012 年的 Ivy Bridge 微架構處理器上使用。
AVX2 指令:2011 年 6 月,Intel 發布了 AVX2 指令集規范,將在 2013 年的 Haswell 微架構處理器上使用。
二、檢測AVX、AVX2
2.1 應用程序如何檢測AVX
在Intel手冊第一卷的“13.5 DETECTION OF AVX INSTRUCTIONS”中介紹了AVX指令集的檢測辦法,具體步驟為——
1) Detect CPUID.1:ECX.OSXSAVE[bit 27] = 1 (XGETBV enabled for application use)
2) Issue XGETBV and verify that XCR0[2:1] = ‘11b’ (XMM state and YMM state are enabled by OS).
3) detect CPUID.1:ECX.AVX[bit 28] = 1 (AVX instructions supported).
(Step 3 can be done in any order relative to 1 and 2)
Intel還給出了匯編偽代碼——
INT supports_AVX() { mov eax, 1 cpuid and ecx, 018000000H cmp ecx, 018000000H; check both OSXSAVE and AVX feature flags jne not_supported ; processor supports AVX instructions and XGETBV is enabled by OS mov ecx, 0; specify 0 for XCR0 register XGETBV ; result in EDX:EAX and eax, 06H cmp eax, 06H; check OS has enabled both XMM and YMM state support jne not_supported mov eax, 1 jmp done NOT_SUPPORTED: mov eax, 0 done:
解釋一下它的檢測步驟——
1) 檢測CPUID.1:ECX.OSXSAVE[bit 27] = 1。該位為1表示操作系統支持XSAVE系列指令,於是在應用程序中可以使用XGETBV等XSAVE系列指令。
2) 使用XGETBV指令獲取XCR0寄存器的值,並檢查第1位至第2位是否都為1。即檢查操作系統是否支持XMM和YMM狀態。
3) 檢測CPUID.1:ECX.OSXSAVE[bit 27] = 1。該位為1表示硬件支持AVX指令集。
XCR0叫做XFEATURE_ENABLED_MASK寄存器,它是一個64位寄存器。它的第0位是x87 FPU/MMX狀態,第1位是XMM狀態,第2位是YMM狀態。如果操作系統支持AVX指令集,它就會將XMM和YMM狀態均置為1。詳見Intel手冊第3卷的“2.6 EXTENDED CONTROL REGISTERS (INCLUDING XCR0)”——

AMD對XCR0寄存器做了擴展,第62位是LWP狀態。詳見AMD手冊第3卷的“11.5.2 XFEATURE_ENABLED_MASK”——

2.2 應用程序如何檢測AVX2
在《Intel® Architecture Instruction Set Extensions Programming Reference》的“2.2.3 Detection of AVX2”中介紹了AVX2指令集的檢測方法和匯編偽代碼,摘錄如下——
Hardware support for AVX2 is indicated by CPUID.(EAX=07H,ECX=0H):EBX.AVX2[bit 5]=1. Application Software must identify that hardware supports AVX as explained in Section 2.2, after that it must also detect support for AVX2 by checking CPUID.(EAX=07H, ECX=0H):EBX.AVX2[bit 5]. The recommended pseudocode sequence for detection of AVX2 is: ---------------------------------------------------------------------------------------- INT supports_avx2() { ; result in eax mov eax, 1 cpuid and ecx, 018000000H cmp ecx, 018000000H; check both OSXSAVE and AVX feature flags jne not_supported ; processor supports AVX instructions and XGETBV is enabled by OS mov eax, 7 mov ecx, 0 cpuid and ebx, 20H cmp ebx, 20H; check AVX2 feature flags jne not_supported mov ecx, 0; specify 0 for XFEATURE_ENABLED_MASK register XGETBV; result in EDX:EAX and eax, 06H cmp eax, 06H; check OS has enabled both XMM and YMM state support jne not_supported mov eax, 1 jmp done NOT_SUPPORTED: mov eax, 0 done: }
可以看出,它是通過三個步奏來檢查AVX2指令集的——
1) 使用cpuid指令的功能1,檢測OSXSAVE和AVX標志。
2) 使用cpuid指令的功能7,檢測AVX2標志。
3) 使用XGETBV指令獲取XCR0寄存器的值,判斷操作系統是否支持XMM和YMM狀態。
2.3 如何獲取XCR0寄存器的值
官方推薦使用XGETBV指令來獲取XCR0寄存器的值。輸入寄存器是ECX,是XCR系列寄存器的索引,對於XCR0來說應填0。輸出寄存器是EDX和EAX,分別是高32位和低32位。
XGETBV指令是在任何訪問級別均可調用的指令,即在Ring3的應用程序層也可使用XGETBV指令。
雖然應用程序層可以使用XGETBV指令,但在實際使用時會遇到問題。這是因為XGETBV是最近才出現的指令,大多數編譯器還不支持XGETBV指令。
該怎么辦呢?
cpuid的0Dh號功能(Processor Extended State Enumeration)就是為這種情況設計的。當使用功能號0Dh、子功能號0調用cpuid指令時,返回的EDX和EAX就是XCR0的值。
2.4 編寫檢測函數
前面我們看到了Intel的檢測AVX與AVX2的匯編偽代碼。雖然將其直接翻譯為VC中的內嵌匯編並不復雜,但存在兩個問題——
1. VC在x64平台不支持內嵌匯編;
2. 使用不方便。它比較適合在編寫匯編代碼時使用,但對於C語言程序來說,我們希望能以更好的方式組織代碼。
這時可以參考先前的simd_sse_level函數的設計,函數的返回值是操作系統對AVX指令集的支持級別,還提供一個指針參數來接收硬件對AVX指令集的支持級別。於是,定義了這些常數——
#define SIMD_AVX_NONE 0 // 不支持
#define SIMD_AVX_1 1 // AVX
#define SIMD_AVX_2 2 // AVX2
我們可以利用先前的CPUIDFIELD方案來簡化檢測代碼的編寫。先定義好相關的常數——
#define CPUF_AVX CPUIDFIELD_MAKE(1,0,2,28,1)
#define CPUF_AVX2 CPUIDFIELD_MAKE(7,0,1,5,1)
#define CPUF_XSAVE CPUIDFIELD_MAKE(1,0,2,26,1)
#define CPUF_OSXSAVE CPUIDFIELD_MAKE(1,0,2,27,1)
#define CPUF_XFeatureSupportedMaskLo CPUIDFIELD_MAKE(0xD,0,0,0,32)
在編寫具體的檢測代碼時,沒必要拘泥於官方的那三個步驟,可以先檢查硬件支持性,然后再檢查操作系統支持性。函數代碼如下——
int simd_avx_level(int* phwavx) { int rt = SIMD_AVX_NONE; // result // check processor support if (0!=getcpuidfield(CPUF_AVX)) { rt = SIMD_AVX_1; if (0!=getcpuidfield(CPUF_AVX2)) { rt = SIMD_AVX_2; } } if (NULL!=phwavx) *phwavx=rt; // check OS support if (0!=getcpuidfield(CPUF_OSXSAVE)) // XGETBV enabled for application use. { UINT32 n = getcpuidfield(CPUF_XFeatureSupportedMaskLo); // XCR0: XFeatureSupportedMask register. if (6==(n&6)) // XCR0[2:1] = ‘11b’ (XMM state and YMM state are enabled by OS). { return rt; } } return SIMD_AVX_NONE; }
三、檢測F16C、FMA、FMA4、XOP
在《Intel® Architecture Instruction Set Extensions Programming Reference》的“2.2.1 Detection of FMA”中介紹了FMA指令的檢測方法和匯編偽代碼,摘錄如下——
Hardware support for FMA is indicated by CPUID.1:ECX.FMA[bit 12]=1. Application Software must identify that hardware supports AVX as explained in Section 2.2, after that it must also detect support for FMA by CPUID.1:ECX.FMA[bit 12]. The recommended pseudocode sequence for detection of FMA is: ---------------------------------------------------------------------------------------- INT supports_fma() { ; result in eax mov eax, 1 cpuid and ecx, 018001000H cmp ecx, 018001000H; check OSXSAVE, AVX, FMA feature flags jne not_supported ; processor supports AVX,FMA instructions and XGETBV is enabled by OS mov ecx, 0; specify 0 for XFEATURE_ENABLED_MASK register XGETBV; result in EDX:EAX and eax, 06H cmp eax, 06H; check OS has enabled both XMM and YMM state support jne not_supported mov eax, 1 jmp done NOT_SUPPORTED: mov eax, 0 done: } ------------------------------------------------------------------------------- Note that FMA comprises 256-bit and 128-bit SIMD instructions operating on YMM states.
可以看出上面的代碼與AVX2的檢測代碼很相似,只是多了對FMA標志位的檢查。
所以我們可以將其分解為兩個步驟,先調用simd_avx_level檢查AVX的支持性,然后再調用getcpuidfield檢查硬件是否支持FMA,即這樣的代碼——
if (simd_avx_level(NULL)>0) { if (getcpuidfield(CPUF_FMA)) { 支持FMA } }
這樣就只需定義F16C、FMA、FMA4、XOP的常數就夠了——
#define CPUF_F16C CPUIDFIELD_MAKE(1,0,2,29,1)
#define CPUF_FMA CPUIDFIELD_MAKE(1,0,2,12,1)
#define CPUF_FMA4 CPUIDFIELD_MAKE(0x80000001,0,2,16,1)
#define CPUF_XOP CPUIDFIELD_MAKE(0x80000001,0,2,11,1)
四、全部代碼
全部代碼——
#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) #define CPUF_AVX CPUIDFIELD_MAKE(1,0,2,28,1) #define CPUF_AVX2 CPUIDFIELD_MAKE(7,0,1,5,1) #define CPUF_OSXSAVE CPUIDFIELD_MAKE(1,0,2,27,1) #define CPUF_XFeatureSupportedMaskLo CPUIDFIELD_MAKE(0xD,0,0,0,32) #define CPUF_F16C CPUIDFIELD_MAKE(1,0,2,29,1) #define CPUF_FMA CPUIDFIELD_MAKE(1,0,2,12,1) #define CPUF_FMA4 CPUIDFIELD_MAKE(0x80000001,0,2,16,1) #define CPUF_XOP CPUIDFIELD_MAKE(0x80000001,0,2,11,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", }; // AVX系列指令集的支持級別. simd_avx_level 函數的返回值。 #define SIMD_AVX_NONE 0 // 不支持 #define SIMD_AVX_1 1 // AVX #define SIMD_AVX_2 2 // AVX2 const char* simd_avx_names[] = { "None", "AVX", "AVX2" }; 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; } // 檢測AVX系列指令集的支持級別. int simd_avx_level(int* phwavx) { int rt = SIMD_AVX_NONE; // result // check processor support if (0!=getcpuidfield(CPUF_AVX)) { rt = SIMD_AVX_1; if (0!=getcpuidfield(CPUF_AVX2)) { rt = SIMD_AVX_2; } } if (NULL!=phwavx) *phwavx=rt; // check OS support if (0!=getcpuidfield(CPUF_OSXSAVE)) // XGETBV enabled for application use. { UINT32 n = getcpuidfield(CPUF_XFeatureSupportedMaskLo); // XCR0: XFeatureSupportedMask register. if (6==(n&6)) // XCR0[2:1] = ‘11b’ (XMM state and YMM state are enabled by OS). { return rt; } } return SIMD_AVX_NONE; } int _tmain(int argc, _TCHAR* argv[]) { int i; //__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(i=1; i<sizeof(simd_sse_names)/sizeof(simd_sse_names[0]); ++i) { if (nhwsse>=i) printf("\t%s\n", simd_sse_names[i]); } // 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)); // test AVX int nhwavx; // 硬件支持AVX. int navx; // 操作系統支持AVX. navx = simd_avx_level(&nhwavx); printf("AVX: %d\t// hw: %d\n", navx, nhwavx); for(i=1; i<sizeof(simd_avx_names)/sizeof(simd_avx_names[0]); ++i) { if (nhwavx>=i) printf("\t%s\n", simd_avx_names[i]); } // test F16C/FMA/FMA4/XOP printf("F16C: %d\n", getcpuidfield(CPUF_F16C)); printf("FMA: %d\n", getcpuidfield(CPUF_FMA)); printf("FMA4: %d\n", getcpuidfield(CPUF_FMA4)); printf("XOP: %d\n", getcpuidfield(CPUF_XOP)); 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® Architecture Instruction Set Extensions Programming Reference》. FEBRUARY 2012. http://software.intel.com/file/41604
《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
《Haswell New Instruction Descriptions Now Available! 》. Mark Buxton. http://software.intel.com/en-us/blogs/2011/06/13/haswell-new-instruction-descriptions-now-available/
[IDF2012]ARCS002《Introduction to the upcoming Intel® Advanced Vector Extensions 2 (Intel® AVX2)》. 王有偉, Henry Ou. 2012-4.
[IDF2012]ARCS002《即將推出的英特爾® 高級矢量擴展指令集2(英特爾® AVX2)介紹》. 王有偉, Henry Ou. 2012-4.
《x86/x64 指令系統》. mik(鄧志). http://www.mouseos.com/x64/default.html
《[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
《[VC] CPUIDFIELD:CPUID字段的統一編號、讀取方案。范例:檢查SSE4A、AES、PCLMULQDQ指令》. http://www.cnblogs.com/zyl910/archive/2012/06/29/getcpuidfield.html
《[C#] cmdarg_ui:“簡單參數命令行程序”的通用圖形界面》. http://www.cnblogs.com/zyl910/archive/2012/06/19/cmdarg_ui.html
