一般来说,这些都是附加的,但请记住,多年来英特尔和AMD对这些产品的支持存在差异.
如果你有AVX,那么你也可以假设SSE,SSE2,SSE3,SSSE3,SSE4.1和SSE 4.2.请记住,要使用AVX,还需要验证OSXSAVE CPUID位是否设置为确保您使用的OS实际上也支持保存AVX寄存器.
您仍应明确检查您在代码中使用的所有CPUID支持的稳健性(例如,同时检查AVX,OSXSAVE,SSE4,SSE3,SSSE3以保护您的AVX代码路径).
#include <intrin.h>
inline bool IsAVXSupported()
{
#if defined(_M_IX86 ) || defined(_M_X64)
int CPUInfo[4] = {-1};
__cpuid( CPUInfo, 0 );
if ( CPUInfo[0] < 1 )
return false;
__cpuid(CPUInfo, 1 );
int ecx = 0x10000000 // AVX
| 0x8000000 // OSXSAVE
| 0x100000 // SSE 4.2
| 0x80000 // SSE 4.1
| 0x200 // SSSE3
| 0x1; // SSE3
if ( ( CPUInfo[2] & ecx ) != ecx )
return false;
return true;
#else
return false;
#endif
}
Run Code Online (Sandbox Code Playgroud)
所有能够支持x64本机的处理器都需要SSE和SSE2,因此它们是所有代码的良好基线假设.即使对于x86体系结构,Windows 8.0,Windows 8.1和Windows 10也明确要求SSE和SSE2支持,因此这些指令集非常普遍.换句话说,如果您检查SSE或SSE2失败,只需退出应用程序并发生致命错误.
#include <windows.h>
inline bool IsSSESupported()
{
#if defined(_M_IX86 ) || defined(_M_X64)
return ( IsProcessorFeaturePresent( PF_XMMI_INSTRUCTIONS_AVAILABLE ) != 0 && IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ) != 0 );
#else
return false;
#endif
}
Run Code Online (Sandbox Code Playgroud)
-要么-
#include <intrin.h>
inline bool IsSSESupported()
{
#if defined(_M_IX86 ) || defined(_M_X64)
int CPUInfo[4] = {-1};
__cpuid( CPUInfo, 0 );
if ( CPUInfo[0] < 1 )
return false;
__cpuid(CPUInfo, 1 );
int edx = 0x4000000 // SSE2
| 0x2000000; // SSE
if ( ( CPUInfo[3] & edx ) != edx )
return false;
return true;
#else
return false;
#endif
}
Run Code Online (Sandbox Code Playgroud)
另外,请记住MMX,x87 FPU和AMD 3DNow!*是x64本机的所有已弃用的指令集,因此您不应再在新代码中主动使用它们.一个好的经验法则是避免使用任何返回__m64或获取__m64数据类型的内在函数.
您可能需要查看此DirectXMath博客系列,其中包含许多这些指令集的注释以及相关的处理器支持要求.
注意(*) - 所有AMD 3DNow!除了PREFETCH和之外,指令已被弃用PREFETCHW.第一代Intel64处理器缺乏对这些指令的支持,但后来被添加,因为它们被认为是核心X64指令集的一部分.Windows 8.1和Windows 10 x64 PREFETCHW特别需要,尽管测试有点奇怪.Broadwell之前的大多数英特尔CPU事实上都没有PREFETCHW通过CPUID 报告支持,但他们将操作码视为无操作,而不是抛出"非法指令"异常.因此,这里的测试是(a)它是由CPUID支持的,(b)如果没有,PREFETCHW至少不会抛出异常.
下面是Visual Studio的一些测试代码,它演示了PREFETCHW测试以及x86和x64平台的许多其他CPUID位.
#include <intrin.h>
#include <stdio.h>
#include <windows.h>
#include <excpt.h>
void main()
{
unsigned int x = _mm_getcsr();
printf("%08X\n", x );
bool prefetchw = false;
// See http://msdn.microsoft.com/en-us/library/hskdteyh.aspx
int CPUInfo[4] = {-1};
__cpuid( CPUInfo, 0 );
if ( CPUInfo[0] > 0 )
{
__cpuid(CPUInfo, 1 );
// EAX
{
int stepping = (CPUInfo[0] & 0xf);
int basemodel = (CPUInfo[0] >> 4) & 0xf;
int basefamily = (CPUInfo[0] >> 8) & 0xf;
int xmodel = (CPUInfo[0] >> 16) & 0xf;
int xfamily = (CPUInfo[0] >> 20) & 0xff;
int family = basefamily + xfamily;
int model = (xmodel << 4) | basemodel;
printf("Family %02X, Model %02X, Stepping %u\n", family, model, stepping );
}
// ECX
if ( CPUInfo[2] & 0x20000000 ) // bit 29
printf("F16C\n");
if ( CPUInfo[2] & 0x10000000 ) // bit 28
printf("AVX\n");
if ( CPUInfo[2] & 0x8000000 ) // bit 27
printf("OSXSAVE\n");
if ( CPUInfo[2] & 0x400000 ) // bit 22
printf("MOVBE\n");
if ( CPUInfo[2] & 0x100000 ) // bit 20
printf("SSE4.2\n");
if ( CPUInfo[2] & 0x80000 ) // bit 19
printf("SSE4.1\n");
if ( CPUInfo[2] & 0x2000 ) // bit 13
printf("CMPXCHANG16B\n");
if ( CPUInfo[2] & 0x1000 ) // bit 12
printf("FMA3\n");
if ( CPUInfo[2] & 0x200 ) // bit 9
printf("SSSE3\n");
if ( CPUInfo[2] & 0x1 ) // bit 0
printf("SSE3\n");
// EDX
if ( CPUInfo[3] & 0x4000000 ) // bit 26
printf("SSE2\n");
if ( CPUInfo[3] & 0x2000000 ) // bit 25
printf("SSE\n");
if ( CPUInfo[3] & 0x800000 ) // bit 23
printf("MMX\n");
}
else
printf("CPU doesn't support Feature Identifiers\n");
if ( CPUInfo[0] >= 7 )
{
__cpuidex(CPUInfo, 7, 0);
// EBX
if ( CPUInfo[1] & 0x100 ) // bit 8
printf("BMI2\n");
if ( CPUInfo[1] & 0x20 ) // bit 5
printf("AVX2\n");
if ( CPUInfo[1] & 0x8 ) // bit 3
printf("BMI\n");
}
else
printf("CPU doesn't support Structured Extended Feature Flags\n");
// Extended features
__cpuid( CPUInfo, 0x80000000 );
if ( CPUInfo[0] > 0x80000000 )
{
__cpuid(CPUInfo, 0x80000001 );
// ECX
if ( CPUInfo[2] & 0x10000 ) // bit 16
printf("FMA4\n");
if ( CPUInfo[2] & 0x800 ) // bit 11
printf("XOP\n");
if ( CPUInfo[2] & 0x100 ) // bit 8
{
printf("PREFETCHW\n");
prefetchw = true;
}
if ( CPUInfo[2] & 0x80 ) // bit 7
printf("Misalign SSE\n");
if ( CPUInfo[2] & 0x40 ) // bit 6
printf("SSE4A\n");
if ( CPUInfo[2] & 0x1 ) // bit 0
printf("LAHF/SAHF\n");
// EDX
if ( CPUInfo[3] & 0x80000000 ) // bit 31
printf("3DNow!\n");
if ( CPUInfo[3] & 0x40000000 ) // bit 30
printf("3DNowExt!\n");
if ( CPUInfo[3] & 0x20000000 ) // bit 29
printf("x64\n");
if ( CPUInfo[3] & 0x100000 ) // bit 20
printf("NX\n");
}
else
printf("CPU doesn't support Extended Feature Identifiers\n");
if ( !prefetchw )
{
bool illegal = false;
__try
{
static const unsigned int s_data = 0xabcd0123;
_m_prefetchw(&s_data);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
illegal = true;
}
if (illegal)
{
printf("PREFETCHW is an invalid instruction on this processor\n");
}
}
}
Run Code Online (Sandbox Code Playgroud)
更新:当然,根本的挑战是如何处理缺乏AVX支持的系统?虽然指令集很有用,但拥有支持AVX的处理器的最大好处是能够使用/arch:AVX构建交换机,该交换机可以全局使用VEX前缀以获得更好的SSE/SSE2代码.唯一的问题是生成的代码DLL/EXE与缺乏AVX支持的系统不兼容.
因此,对于Windows,理想情况下,您应该为非AVX系统构建一个EXE(假设仅使用SSE/SSE2 /arch:SSE2代替x86代码;此设置对于x64代码是隐式的),这是针对AVX优化的不同EXE(使用/arch:AVX) ,然后使用CPU检测来确定给定系统使用哪个EXE.
幸运的是,我们可以随时使用Xbox One,/arch::AVX因为它是一个固定的平台......
| 归档时间: |
|
| 查看次数: |
448 次 |
| 最近记录: |