在X86下用C++完成
如何检测CPU支持的指令集,这是个有趣的问题。
起因是我在写一个用simd加速向量计算的库。
我找了很久,发现了一个C函数,叫做void __cpuid(int[4], int);
,在头文件<intrin.h>
中
这里我觉得有必要引入一个概念,叫intrinsic function,它指的是一类可以直接从C函数翻译成对应汇编指令的函数。相比汇编来说,使用者不用去管寄存器,调用约定,只要把参数传给Intrinsic function就行了。
__cpuid
函数就是这么一个intrinsic,其对应的汇编指令为CPUID
。
CPUID
的用法是:先往EAX中输入一个参数,然后CPU会往四个寄存器EAX,EBX,ECX,EDX
返回对应的信息。
__cpuid
函数的第一个参数int[4]
代表了四个寄存器,EAX,EBX,ECX,EDX
,在这里是作为返回值用的,第二个参数int
代表了EAX,这里是作为输入参数。
对于输入什么,返回什么,什么代表什么,我找了参考的资料 : cpuid
有个最简单的把戏:往eax里输入00H,然后就会按EBX,EDX,ECX的顺序返回CPU的制造商
int rgstr[4];
memset(rgstr, 0x00000000, 4 * sizeof(int));
__cpuid(rgstr, 0x00000000);
char vendor[17];
memset(vendor, '\0', 14);
memcpy(vendor, rgstr + 1, sizeof(int));
memcpy(vendor + sizeof(int), rgstr + 3, sizeof(int));
memcpy(vendor + 2 * sizeof(int), rgstr + 2, sizeof(int));
如果输出字符数组vendor就能看到结果:
GenuineIntel
当然这是Intel cpu的输出
对于AMD会输出 : AuthenticAMD
首先,把EAX输入01H
也就是:
__cpuid(rgstr, 0x00000001);
然后根据参考资料上的figure3-7可以知道哪个寄存器的哪一位代表了什么。
比如说从表里可知,ECX的第0位代表SSE3的支持
那么如果ECX & 0x00000001 == 1 => CPU支持SSE3指令集。
所以可以这么写代码
std::bitset<32> bitInfo(rgstr[3]);
if (bitInfo[0]) {
SSE3Support = true;
}
所以对着表去写代码吧^_^
其实吧...如果当你想检测AVX2的时候就会发现不对劲。
Table3-8中说只要EAX输入了07H就能在EBX里拿到一堆信息!
然后我兴致勃勃的写下了代码,震惊的发现 -- 什么,我的CPU不支持AVX2
o(≧口≦)o
(╯°□°)╯︵ ┻━┻
╰(‵□′)╯
好吧其实是这样的
它边上有一行小字-> Structured Extended Feature Flags Enumeration Leaf (Output depends on ECX input value)
意思是这东西还得根据ECX的值来返回不同的值。
但问题是__cpuid只有两个输入,怎么办?
然后我发现有那么个函数叫做
void __cpuidex(int[4], int, int);
这个函数前两个参数与cpuid无异,第三个参数就是ECX了。
顺带一提,当ECX=00H时,这个函数和__cpuid的功能是一样的。
当然不是所有人都喜欢写一大串代码的....
所以你可以用微软写的示例
(●'◡'●)