Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5833467
  • 博文数量: 671
  • 博客积分: 10010
  • 博客等级: 上将
  • 技术积分: 7310
  • 用 户 组: 普通用户
  • 注册时间: 2006-07-14 09:56
文章分类

全部博文(671)

文章存档

2011年(1)

2010年(2)

2009年(24)

2008年(271)

2007年(319)

2006年(54)

我的朋友

分类: C/C++

2007-10-15 11:07:25

MMX指令集简介(一)


从今天起开始介绍一下MMX指令集。

MMX技术大家肯定是如雷贯耳,也不用我多废话了。总之,它是SIMD(单指令多数据)技术的一个最简单的部分,它只能对整数(BYTE、WORD、DWORD、QWORD)进行操作,而且提供的指令也有限,不象Intel原来的指令集那么复杂。但是我们可敬的雷锋叔叔不是说过“要把有限的生命投入到无限的为人民服务之中去”么?我们这里也是一样,要把有限的指令应用到无限的计算方法当中去。简单有限的指令组合起来,一样可以完成近乎无限的功能。不信可以去Intel的网站()看看,那里有非常多的应用MMX技术来优化了的算法,包罗了图像、图形、音频、通讯、信号处理等几乎所有现在比较流行的技术。当然SSE和SSE2又更进了一步,不过我们现在是讨论MMX,先把这个搞定对于理解其他的SIMD技术肯定是大有帮助。

好了,废话少说,开始进入正题。
先来讲一下MMX寄存器。MMX寄存器实际上用的就是浮点寄存器(就是原来的ST0、……、ST7)的低64Bit(至少一直到PII还是这样,PIII是不是这样我就不知道了),分别被命名为MM0、MM1、……、MM7,每个寄存器都可以被看作是8个BYTE,4个WORD、2个DWORD或1个QWORD,具体以什么为单位取决于所用的指令。MMX指令可以把这几个数据在一条指令里同时操作,而且相互间各不相干,例如下图所示:
 ┌───┬───┬───┬───┐
 │ A1 │ A2 │ A3  │ A4  │
 └───┴───┴───┴───┘
 ┌───┬───┬───┬───┐
 │ B1 │ B2 │ B3  │ B4  │
 +└───┴───┴───┴───┘
 ──────────────────
 ┌───┬───┬───┬───┐
 │A1+B1 │A2+B2 │A3+B3 │A4+B4 │
 └───┴───┴───┴───┘
这对数据密集型的计算(比如图像处理)是个相当大的喜讯。但是因为它实际使用的是浮点寄存器,所以不能和浮点运算同时执行(占了人家的房子当然不好意思了),要执行浮点指令前必须执行一条EMMS指令,通知CPU“我的租期到了,现在该把房子还给你了”。这么一条指令也是要花时钟周期的(可能还绝对不止一个时钟周期),要是频繁的切换MMX状态和浮点状态,从MMX技术获得的这么点利润就又全陪进去了。所以在写代码的时候要注意,务必使MMX指令的运算和浮点运算分开来,个人管个人,免得一个房子两个人挤。

下面介绍MMX指令。
在MMX指令集里加入了几个原来Intel指令集里没有(当然没有了,我指的是没有类似的运算指令)的非常好用的指令,我以前搞过图像处理,最能理解这几个指令的好处,就先从这几个指令讲起。

1.饱和运算
所谓饱和运算,就是当运算结果大于一个上限或小于一个下限时,结果就等于上限或是下限。例如:BYTE运算,最大值是255,0xF1+0x35应该是等于0x26,但由于结果大于255,那么饱和运算的结果就是0xFF。在图像处理里经常有(比如说增加亮度)两种灰度值运算后要判断只是否大于255或小于0,根据结果再取255或0,又是if又是什么的。现在只要一条指令就OK了。
这几条指令分别是:

PADDS[B,W] 饱和有符号数加[byte, word]
PADDUS[B,W] 饱和无符号数加[byte, word]
PSUBS[B,W] 饱和有符号数减[byte, word]
PSUBUS[B,W] 饱和无符号数减[byte, word]

是不是很方便啊!(有符号数就是有正有负,一个BYTE就是-128~127;无符号数就是都是正的,一个BYTE就是0~255)
注:PADDS[B,W]的意思就是PADDSB和PADDSW的简写,以下都将这样写。

先讲到这里,下一篇再接着讲。(敲的累死了,以后肯定不写书了)

MMX指令集简介(二)


(上一篇忘记说明了,这些所有的MMX指令的目的操作数——就是放在前面的——一般都是MMX寄存器,源操作数——就是放在后面的——一般是MMX寄存器或内存,以下除非有特殊说明,均有这个限制)

2.向量点乘指令
什么是向量点乘我想不用我说了吧,学过高数的都该知道。这条指令的格式如下:

PMADDWD 向量点乘运算(word)

其实际操作为:
┌───┬───┬───┬───┐
│  A1  │  A2  │  A3  │  A4  │
└───┴───┴───┴───┘
┌───┬───┬───┬───┐
│  B1  │  B2  │  B3  │  B4  │
└───┴───┴───┴───┘ PMADDWD
──────────────────────
┌───────┬───────┐
│  A1*B1+A2*B2 │  A3*B3+A4*B4 │
└───────┴───────┘

这条指令的用处不要我说了,高数里有好多这种运算。我只是感叹,这么好用的指令为什么Intel不早点想出来呢?

3.串比较指令
这类指令有两个

PCMPEQ[B,W,D] 串等于比较(byte,word,dword)
PCMPGT[B,W,D] 串大于比较(byte,word,dword)

举个例子说明实际操作
┌───┬───┬───┬───┐
│ 0xF0 │ 0xA2 │ 0x24 │ 0x75 │
└───┴───┴───┴───┘
┌───┬───┬───┬───┐
│ 0xF0 │ 0x2C │ 0x24 │ 0x54 │
└───┴───┴───┴───┘PCMPEQ
─────────────────────
┌───┬───┬───┬───┐
│ 0xFF │ 0x00 │ 0xFF │ 0x00 │
└───┴───┴───┴───┘

┌───┬───┬───┬───┐
│ 100  │  70  │   6  │  18  │
└───┴───┴───┴───┘
┌───┬───┬───┬───┐
│  40  │  120 │   5  │  26  │
└───┴───┴───┴───┘PCMPGT
─────────────────────
┌───┬───┬───┬───┐
│ 0xFF │ 0x00 │ 0xFF │ 0x00 │
└───┴───┴───┴───┘

看明白了?结果为全1,即说明比较结果为TRUE;结果为全0,说明比较结果为FALSE。这条指令用在什么地方?字符串快速比较(Intel网站上有源码,扬言速度提高50%)、模拟Color Key(具体怎么实现不在本文讨论范围之内,自己想一想,我以后可能会介绍一下)等等好多方面。

接下来的指令就比较普通了,但还是有非常大的便宜可以占的。

4.数据传输

MOV[D,Q] 数据传输(dword,qword)

MOVD就是传32个Bit(传到MMX寄存器的低32Bit或只传输MMX寄存器的低32Bit),MOVQ就是传64个Bit,这条指令是可以在MMX寄存器、通用寄存器(就是EAX、ESI之类,但是这些寄存器只能用在MOVD里——一直到PIII都还是32Bit的通用寄存器嘛)和内存之间来回折腾的,没什么限制。

今天就到这儿吧。

MMX指令集简介(三)


5.算术指令

这里的指令和普通的算术指令没有什么运算规则上的区别,只不过是在一条指令里同时处理几个数据而已。指令如下:
PADD[B,W,D] 无符号数加[byte,word,dword]
PADDS[B,W] 有符号数加[byte,word]
PSUB[B,W,D] 无符号数减[byte,word,dword]
PSUBS[B,W] 有符号数减[byte,word]
PMULHW 有符号数乘取高位[word]
PMULLW 有符号数乘取低位[word]

值的提一下的是那两个乘法指令,因为两个WORD相乘,结果是个DWORD,但在MMX指令里又要保持数据的一致性,不可能是交给它4个数据,运算完了就剩下2个数据了,于是Intel就搞了这么个方法,乘法嘛,要算两次,一次只把高位给你,另一次再把低位给你——怎么,不服气么?哼,我给了你这么多方便又好用的指令,才讹了你这么点儿时钟周期就不满意了?CPU是我做还是你做?哼,没话说了吧!——没办法,人家是老大,该怎样就怎样了。不过凡事要往两方面想,假如我们要判断两数是否同号,只要乘完了取高位就行了,低位嘛,在这儿用不上,随它怎么样了,而且还省了个寄存器。
这两条指令的实际操作如下(只写一条就行了):

┌───────┬───┬───┐
│      A1      │  …  │  …  │
└───────┴───┴───┘
┌───────┬───┬───┐
│      B1      │  …  │  …  │
└───────┴───┴───┘PMULHW
─────────────────────
┌───────┬───┬───┐
│HIWORD(A1*B1) │  …  │  …  │
└───────┴───┴───┘

但是大家发现没有,这里没有除法运算,也没有无符号数的乘法运算,不要急,PIII的SSE指令集里就有了,大家升级CPU啊!

6.逻辑指令
这些指令和原来我们学过的8086汇编里的逻辑指令完全相同(除了PANDN相当于原先的两个操作——与和非——的结合),只是把寄存器换成64位的而已。

PAND 按位与操作
PANDN 按位与后再取非操作
POR 按位或操作
PXOR 按位异或操作

7.移位指令
移位指令也不要我多说了,实际就是更方便(方便嘛当然就要有点儿限制了)的乘除法运算。但是却没有BYTE为单位的移位运算,不知道是为什么。

PSLL[W,D,Q] 逻辑左移[word,dword,qword]
PSRL[W,D,Q] 逻辑右移[word,dword,qword]
PSRA[W,D,Q] 算术右移[word,dword,qword]

再提一句,这条指令的源操作数(就是放在后面的那个)可以是个立即数,就像
PSLLW MM0, 2
这样,就是每个WORD左移2位。还有,当源操作数是MMX寄存器或是内存地址时,CPU是把它当成一个数来看的,注意了,不要以为还是当分开的数据看,对应的单元移位对应的位数(我曾经以为会是这样,但事实证明我是错的),那有这种美事?

好了,今天就讲这么多吧!还有最后一类指令,留到下次吧!
 

补充日期: 2000-11-17 17:21:19

差点儿误人子弟,修正一下

PANDN指令的操作是:将目的数取非再和源数做与操作,用C语言表示为

dest = (~dest) & src

对不起,这条指令我原来没用过,想当然了,抱歉抱歉。

MMX指令集简介(四)


8.数据转换指令

现在已经讲了绝大部分的MMX指令,大家看到了吧,每个指令前都有个“P”开头。这个“P”的意思就是——Packet,就是说每个寄存器里的数据都是个数据包,而不是一个数据。那么这些指令就是把这些数据打包或拆包(我不知该怎么下这个操作的定义)用的。这些指令的操作都有点儿怪异,倒需要详细说明一下。
PACKUSWB 有符号数WORD带饱和压缩成无符号BYTE
PACKSS[WB,DW] 有符号数带饱和压缩成有符号数[word->byte,dword->word]
PUNPCKH[BW,WD,DQ] 交错放置两数的高位[byte->word,word->dword,dword->qword]
PUNPCKL[BW,WD,DQ] 交错放置两数的低位[byte->word,word->dword,dword->qword]

先说一下PACKUSWB。举个例子,PACKUSWB MM0, MM1,其实际操作为(图示为高位在左,低位在右),

┌───┬───┬───┬───┐    ┌───┬───┬───┬───┐
MM1│  A   │  B   │  C   │  D   │    │  E   │  F   │  G   │  H   │MM0
└───┴───┴───┴───┘    └───┴───┴───┴───┘
│                                                              │
└─────────────┐      ┌─────────────┘
│      │
┌─┬─┬─┬─┬─┬─┬─┬─┐
│A │B │C │D │E │F │G │H │MM0
└─┴─┴─┴─┴─┴─┴─┴─┘
不知道大家有没有看明白。大致意思就是:目的数紧缩到低位,源操作数紧缩到高位。另外,这个指令是带饱和操作的,就是说大于255的紧缩后就变成255,小于0的紧缩后就变成0。
PACKSS[WB,DW]的操作和PACKUSWB一样,不过多了个[DW],就是从DWORD紧缩成WORD。

再说一下拆包操作。名字虽然是UNPACK,实际好像不是PACK的逆操作,反倒有点怪,这个操作和乘法操作有一点点类似,也有高位和低位之分的。比如说:PUNPCKHBW MM0, MM1,其实际操作为:

┌─┬─┬─┬─┬─┬─┬─┬─┐    ┌─┬─┬─┬─┬─┬─┬─┬─┐
MM1│A1│B1│C1│D1│E1│F1│G1│H1│    │A2│B2│C2│D2│E2│F2│G2│H2│MM0
└─┴─┴─┴─┴─┴─┴─┴─┘    └─┴─┴─┴─┴─┴─┴─┴─┘
│                                                                  │
└──────────────┐      ┌──────────────┘
│      │
┌─┬─┬─┬─┬─┬─┬─┬─┐
│A1│A2│B1│B2│C1│C2│D1│D2│MM0
└─┴─┴─┴─┴─┴─┴─┴─┘
有了PACKUSWB作基础,大家应该看懂这个图示了吧。这明明不是什么拆包操作是吧!唉,我不过是为了讲究名称的匹配,再说它的指令助记符也写着是UNPACK嘛,不就是个名称,你又何必认真呢?PUNPCKL[BW,WD,DQ]的操作和PUNPCKH类似,就是把两个数的低位交错着放到目的数里。

OK,到此为止,MMX指令集简介正式结束,谢谢大家。

阅读(2678) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~