CPU采用SIMD技术大大提高计算速度,特别是在矩阵运算上。Intel和AMD都在不同程度上实现了这样的技术,Intel拥有从最早的MMX,到SSE,SSE2;AMD是3D NOW!和3D NOW PRO,作为程序员学习下也挺好,说不定哪天优化代码的时候就用上了。
SIMD全称是Single Instruction Multiple Data,就是单个指令能够处理多个数据包。我的理解是,在SIMD之前,寄存器在对数组中各元素做加减的时候,必须按次序取出,放入相应寄存器,然后再相加,举个例子:
有两个数组 a[2] = {2,3}, b[2] = {3,4},然后
MOV ESI , a MOV ESI , a+1
MOV EDI , b MOV EDI , b+1
MOV EAX , [ESI] MOV EAX , [ESI]
MOV EBX , [EDI] MOV EBX , [EDI]
ADD EAX ,EBX ;完成了数组中a[0]和b[0]的相加运算 ADD EBX , EAX; 完成数组中a[1]和b[1]的相加运算
一共进行了两组运算,假如运用SIMD进行运算的话
MOV ESI a
MOV EDI b
MOVAPS MMX0 [ESI]
MOVAPS MMX1 [EDI]
PADDD MMX0 MMX1 ;一组指令就行了。
MMX 允许一次向MMX寄存器中存储最大64bits的数据,比如一个vector,数据值必须是整数,而且占用的是FPU运算单元。
3DNOW 比MMX好的一点是可以运用浮点数运算。
SSE 从PentiumIII开始新加入了8个寄存器,可以存储最大到128bits的一组数据,能够迅速进行vector4运算
3DNOW PRO基本等于SSE
SSE2从名字上看比SSE更强大了,具体怎么个强大没去想,先放着了。
怎样测试自己的CPU支持哪种SIMD功能呢?这需要用到CPUID这条指令。还是先定义一个数组准备接受测试的结果
typedef struct CPUINFO_TYP {
bool bSSE;
bool bSSE2;
bool b3DNOW;
bool bMMX;
char name[48]; //cpu name
bool bEXT; //扩展功能
bool MMXEX; //AMD的扩展
bool 3DNOW; //Intel的扩展
char vendor[12]; //vendor name
}CPUINFO;
检测时候,只需要喂给EAX一个值,然后调用CPUID指令,相应结果就会返回。比如,我向EAX送去0,CPUID指令就会返回不超过12个字的制造商名称,分别向EBX,EDX,ECX返回4个字;如果向EAX送去1,CPUID会返回CPU功能的一个列表,并且,如果CPU属于Intel,则返回brandID到EBX,如果是AMD,则返回brandID到EAX。如果送入的值是Ox80,那么即将检查的是CPU有无扩展功能,送入0x80到EAX后,CPUID会返回一个值回到EAX,如果该值大于0x80,那就再送出0x80000001到EAX再执行CPUID,我们就可以得到这些扩展功能的描述。如果是IntelCPU,就没必要找扩展功能了,如果是AMD,可以注意下有没有extended MMX和enhanced 3D NOW.
好!那么现在就写个函数来实现这些繁琐的不知道有没钱赚的功能吧!!!(TMD>_____< TMD)
CPUINFO GetCPUInfo() {
CPUINFO info;
char* pStr = info.vendor;
int n=1;
int* pn = &n;
memset(&info, 0, sizeof(CPUINFO));
//1:Vendor name, SSE2, SSE, MMX support
_try{
_asm{
mov eax, 0 //送入零取得vendor name
CPUID
mov esi, pStr
mov [esi], ebx //前四个字符
mov [esi+4], edx //再4个
mov [esi+8], ecx //最后4个
mov eax, 1 //功能表
CPUID
test edx, 04000000h //test SSE2
jz _NOSSE2
mov [info.bSSE2], 1 //true
_NOSSE2: test edx, 02000000h //test SSE
jz _NOSSE
mov [info.bSSE], 1 //true
_NOSSE: test edx, 00800000h //test MMX
jz _EXIT1
mov [info.bMMX], 1 //true
_EXIT1: //done
}
}_except(EXCEPTION_EXECUTE_HANDLER) {
if(_exception_code() == STATUS_ILLEGAL_INSTRUCTION)
return info;
retrun info;
}
//检测CPU的扩展功能
_asm {
mov eax, 80000000h //有扩展功能吗?
CPUID
cmp eax, 80000000h //>0x80吗?
jbe _EXIT2 //没有扩展,跳出
mov [info.bEXT], 1 //有扩展
mov eax, 80000001h
CPUID
test edx, 80000000h //检测有没有3DNOW 功能
jz _EXIT2
mov [info.b3DNOW], 1
_EXIT2:
}
//开始测试CPU属于Intel系还是AMD系
if((strncmp(info.vendor, "GenuineIntel" , 12)==0) && info.bEXT) {
_asm {
mov eax,1
CPUID
mov esi, pn //得到brand-id
mov [esi], ebx
}
int m = 0;
memcpy(&m, pn, sizeof(char)) //只拷贝低8位
n = m;
}
else if (( strncmp( info.vendor, "AuthenticAMD",12)==0 && info.bEXT)
{
_asm{
mov eax, 1
CPUID
mov esi, pn
mov [esi], eax
mov eax, 0x80000001 //取得扩展功能描述
CPUID
test edx, 0x40000000 //extended 3DNOW
jz _AMD1
mov [info.b3DNOWEX], 1
_AMD1: test edx, 0x00400000 //extended MMX
jz _AMD2
mov [info.bMMXEX], 1
_AMD2:
}
}
else{
if (info.bEXT)
//不知道是哪个系的
else
//没扩展功能
}
info.vendor[13] = '/0';
GetCPUName(info.name, n, info.vendor);
return info;
}
不短,逻辑还算清楚,留着了。。。
参考书: << 3d game engine programming-2004>> 作者Stefan Zerbst & Oliver Duvel
3218

被折叠的 条评论
为什么被折叠?



