山本ワールド
Windowsプログラミング
アルゴリズム Vitual C++ 2008/2013によるWin32/Win64 APIレベルのプログラム 基礎 Vitual C++ 2008/2013によるAPIレベルのプログラム(32/64bit) Wix3でインストーラーを作る Visual C++ 2008 Standard Editonによるフォームアプリケーションのプログラム(32/64bit) Vitual C++ 2008 Standard EditonによるAPIレベルのプログラム(32/64bit) Windows 7対応 Visual C++ 2008 ExpressによるAPIレベルのプログラム Visual C++ 2005 ExpressによるAPIレベルのプログラム Visual C++ Versiosn 5 BORLAND C++ Windowsプログラム全般 Excel VBA その他CPUがサポートしているSIMD(MMX,SSE,SSE2,SSE3,SSE3,AVX,AVX2)とWindowsのサポート状況を表示(32/64bit)
概要
CPUおよびWindowsがサポートしているSIMD命令(MMX,SSE,SSE2,SSE3,AVX,AVX2)の状況を表示します。
1行目は実行ファイルをビルドした環境を表示しています。例では、リリースバージョンでWin32向け、ランタイムはマルチスレッドの場合です。
2行目はCPUID命令によりプロセッサストリングを取得し表示しています。
3行目はCPUがサポートするSIMD系の命令を表示しています。
4行目はWindowsがAVXをサポートしているか表示しています。
現在のOSはマルチスレッドで動作するため拡張命令に伴いレジスタが拡張されていると、スレッドの切り替え時にレジスタの退避および復帰を行う必要があります。拡張レジスタに対応していないとプログラムの動作がおかしくなります。
AVXの場合、Windows 7 Service Pack 1以上が必要となります。
Visual C++ 2013で普通にビルドするとWindows Vista以上で実行可能なEXEファイルが作成されます。
Visual C++ 2013でWindows XPで実行可能なEXEファイルを作成するには、プロジェクトのプロパティ 構成のプロパティ 全般 プラットフォームツールセットをVisual Studio 2013 - Windows XP (v120_xp)に設定します。
ソースコードは#if 1800 <= _MSC_VER を#if 0に修正します。
Visual C++ 2008ではそのままビルドすればWindows XP以上で実行可能なEXEファイルが作成されます。
AVX2命令サポートの確認ファンクション番号が間違っていましたので修正しました。 2017/05/21
テスト環境
コンパイラ
Visual C++ 2008/2013 Express 32/64bit マルチバイト/UNICODE実行環境
クリックすると実行したときの画面が表示されます。本ソフトの実行状況、マイコンピュータのプロパティおよびCPU-Zを実行しプリントスクリーンした状態です。
Core i3-370MのCPU-Zの表示が一部i5になっています。バグかと思われます。
Windows XP Home Edition Service Pack 3 & Intel Pentium4 641(Cedar Mill)
Windows XP Home Edition Service Pack 3 & Intel Core2Duo E6320(Conroe)
Windows 7 Enterprise Service Pack 1 64bit & Intel Core i7-3820(Sandy Bridge-E)
Windows 8.1 Enterprise 64bit & Intel Core i3-370M(Arrandale)
Windows 10 Home & Intel Core M-5Y71(Broadwell-Y)
Windows 10 Home & Intel Atom Z3775(Baytrail-T)
なおVirtualBox(バージョンによりサポートしているSIMD命令が異なる)上で動作させた場合、たとえばIntel Core i7-3820の場合、AVXをサポートしているにもかかわらずVirtualBoxがサポートしていないので、本プログラムの実行結果はCPUサポートの表示にAVXが現れないことがあります。
プログラムソースの概要
extinstchk.cpp
WinMain
ビルド環境は定義済みマクロ(_DEBUG,_WIN64,_DLL,_MSC_VER)より取得しています。SIMD命令のサポート状況は、__cpuid関数を使いCPUID命令を発行しています。インラインアセンブラではないので、X64向けにも使えます。
WindowsのAVX命令のサポート状況は、IsWindows7SP1OrGreater関数で判断しています。
IsWindows7SP1OrGreater
この関数はVisual C++ 2013には標準で含まれているので、Visual C++ 2013でビルドした場合は、ソース内で定義されないように#if 1800 <= _MSC_VERでチェックしています。実態は、ヘッダーファイルのVersionHelpers.hでインライン関数で定義されておりVerifyVersionInfoW APIを呼び出しているだけです。
Visual C++ 2008でビルドするとエラーが出るため、VerifyVersionInfoW APIを呼び出すコードを有効にしています。
_MSC_CVER
コンパイル時のVisual C++ のバージョンはマクロ_MSC_VERで取得できますが、 Visual C++の製品バージョンの値を直接反映していないので、#if ~ #endifにより該当する製品バージョンを代入するコードのみ有効にしています。ソースコード
extinstchk.cpp
// SIMD拡張命令のサポート状況を表示
// Visual C++ 2008,2013
#include <windows.h>
#include <tchar.h>
#include <intrin.h>
#if 1800 <= _MSC_VER // Visual C++ 2013以上
#include <VersionHelpers.h>
#else
#ifndef _WIN32_WINNT_WIN7
#define _WIN32_WINNT_WIN7 0x0601
#endif
inline bool IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor){
OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, { 0 }, 0, 0 };
DWORDLONG const dwlConditionMask = VerSetConditionMask(
VerSetConditionMask(
VerSetConditionMask(
0, VER_MAJORVERSION, VER_GREATER_EQUAL),
VER_MINORVERSION, VER_GREATER_EQUAL),
VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
osvi.dwMajorVersion = wMajorVersion;
osvi.dwMinorVersion = wMinorVersion;
osvi.wServicePackMajor = wServicePackMajor;
return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
}
inline bool IsWindows7SP1OrGreater(void){
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 1);
}
#endif
// CPUID 1 ECX
#define AVX_READY (1<<28)
#define SSE42_READY (1<<20)
#define SSE41_READY (1<<19)
#define SSSE3_READY (1<<9)
#define SSE3_READY (1)
// CPUID 1 EDX
#define IA64_READY (1<<30)
#define HTT_READY (1<<8)
#define SSE2_READY (1<<26)
#define SSE_READY (1<<25)
#define MMX_READY (1<<23)
#define FPU_READY (1)
// CPUID 7 0 EBX
#define AVX2_READY (1<<5)
inline int _MSC_CVER(void) {
int cver = 0;
#if _MSC_VER==1400
cver = 2005;
#endif
#if _MSC_VER==1500
cver = 2008;
#endif
#if _MSC_VER==1600
cver = 2010;
#endif
#if _MSC_VER==1700
cver = 2012;
#endif
#if _MSC_VER==1800
cver = 2013;
#endif
return cver;
}
int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow){
TCHAR cpuname[4*4*4];
TCHAR inst[256];
TCHAR buf[256];
int cver = 0;
TCHAR* project;
TCHAR* win;
TCHAR* dll;
#ifdef _DEBUG
project = _TEXT("Debug");
#else
project = _TEXT("Release");
#endif
#ifdef _WIN64
win=_TEXT("x64");
#else
win = _TEXT("Win32");
#endif
#ifdef _DLL
dll = _TEXT("マルチスレッドDLL");
#else
dll = _TEXT("マルチスレッド");
#endif
cver=_MSC_CVER();
_stprintf_s(buf, sizeof(buf) / sizeof(TCHAR), _TEXT("ビルド Visual C++ %i %s %s %s¥r¥n"), cver,project,win,dll);
int CPUInfo[4];
inst[0] = 0;
cpuname[0] = 0;
__cpuid(CPUInfo, 0);
int idmax = CPUInfo[0]; // EAX
__cpuid(CPUInfo, 0x80000000);
if (0x80000002 <= CPUInfo[0]){ // プロセッサブランド文字列の取得が可能である
int CPUInfoV[3][4];
__cpuid(CPUInfoV[0], 0x80000002);
__cpuid(CPUInfoV[1], 0x80000003);
__cpuid(CPUInfoV[2], 0x80000004);
char* p = (char*)CPUInfoV;
for (int n = 0; n < 4*4*3; n++){
cpuname[n] = *p++;
}
_tcscat_s(buf, sizeof(buf) / sizeof(TCHAR), cpuname);
_tcscat_s(buf, sizeof(buf) / sizeof(TCHAR), _TEXT("¥r¥n"));
}
if (1 <= idmax){
__cpuid(CPUInfo, 1);
if (CPUInfo[3] & MMX_READY){
_tcscat_s(inst, sizeof(inst) / sizeof(TCHAR), _TEXT("MMX "));
}
if (CPUInfo[3] & SSE_READY){
_tcscat_s(inst, sizeof(inst) / sizeof(TCHAR), _TEXT("SSE "));
}
if (CPUInfo[3] & SSE2_READY){
_tcscat_s(inst, sizeof(inst) / sizeof(TCHAR), _TEXT("SSE2 "));
}
if (CPUInfo[2] & SSE3_READY){
_tcscat_s(inst, sizeof(inst) / sizeof(TCHAR), _TEXT("SSE3 "));
}
if (CPUInfo[2] & SSSE3_READY){
_tcscat_s(inst, sizeof(inst) / sizeof(TCHAR), _TEXT("SSSE3 "));
}
if (CPUInfo[2] & SSE41_READY){
_tcscat_s(inst, sizeof(inst) / sizeof(TCHAR), _TEXT("SSE4.1 "));
}
if (CPUInfo[2] & SSE42_READY){
_tcscat_s(inst, sizeof(inst) / sizeof(TCHAR), _TEXT("SSE4.2 "));
}
if (CPUInfo[2] & AVX_READY){
_tcscat_s(inst, sizeof(inst) / sizeof(TCHAR), _TEXT("AVX "));
}
}
if (7 <= idmax){
__cpuidex(CPUInfo, 7, 0);
if (CPUInfo[1] & AVX2_READY){
_tcscat_s(inst, sizeof(inst) / sizeof(TCHAR), _TEXT("AVX2 "));
}
}
if (inst[0]){
_tcscat_s(buf, sizeof(buf) / sizeof(TCHAR), _TEXT("CPUサポート "));
_tcscat_s(buf, sizeof(buf) / sizeof(TCHAR), inst);
_tcscat_s(buf, sizeof(buf) / sizeof(TCHAR), _TEXT("¥r¥n"));
}
if (IsWindows7SP1OrGreater()){
_tcscat_s(buf, sizeof(buf) / sizeof(TCHAR), _TEXT("OS AVX Suport"));
}
else{
_tcscat_s(buf, sizeof(buf) / sizeof(TCHAR), _TEXT("OS AVX Not Suport"));
}
MessageBox(0, buf, _TEXT("システムのSIMD命令のサポート状況"), MB_OK);
return (int)0;
}