I MMX簡介
Intel的MMX™技術是對Intel體系結構(IA)指令集的擴展。該技術使用了單指令多數據技術(SIMD)技術,以並行方式處理多個數據元素,從而提高了多媒體和通訊軟件的運行速度。MMX™指令集增加了57條新的操作碼和一個新的64位四字數據類型。
MMX™技術提高了很多應用程序的執行性能,例如活動圖像、視頻會議、二維圖形和三維圖形。幾乎每一個具有重複性和順序性整數計算的應用程序都可以從MMX™技術中受益。對於8位、16位和32位數據元素的處理,改善了程序的性能。一個MMX™指令可一次操作8個字節,且在一個時鐘週期內完成兩條指令,也就是說,可在一個時鐘週期內處理16個數據元素。另外,爲增強性能,MMX™技術爲其它功能釋放了額外的處理器週期。以前需要其它硬件支持的應用程序,現在僅需軟件就能運行。更小的處理器佔用率給更高程度的併發技術提供了條件,在當今衆多的操作系統中這些併發技術得到了利用。在基於Intel的分析系統中,某些功能的性能提高了50%到400%。這種數量級的性能擴展可以在新一代處理器中得到體現。在軟件內核中,其速度得到更大的提高,其幅度爲原有速度的三至五倍。
MMX的缺點:由於MMX的運算指令必須在數據配對整齊的時候才能使用,所以使用MMX指令要比普通的彙編指令多餘許多分組配對的指令,如果運算不是特別的整齊的話,就要浪費大量的時間在數據的配對上,所以說MMX指令也不是萬能的,也有其很大的缺陷。同時MMX指令在處理16位數據的時候才能發揮最大的作用,處理8位數據要有一點技巧。而處理32位數據,MMX指令幾乎沒有什麼加速能力。(考慮分組耗時的話)
II MMX基本指令集
具體細節請參閱《INTEL
體系結構MMX技術程序員參考手冊》第五章
2.1
拷貝指令
movq:64位數據拷貝,如果內存8位對齊的話,是一個64位寫,否則2個32位寫。
movd:32位數據拷貝,注意:如果從內存向MMX寄存器拷貝,MMX高32位清零!
2.2
分組指令
分組指令是MMX特有的,所以對於它我們要特別的關注。分組指令基本上可以分爲2類,一類是不帶符號緊縮的,一類是帶符號緊縮的。現在我們分別予以介紹:
①punpcklbw / punpcklwd / punpckldq (l表示低位分組,bw8位,wd16位,dq32位):它是簡單的將兩個MMX寄存器的低32位交錯組合爲一個64位數據。所以它是不能將長數據轉換爲短數據的。
②packuswb 將16位數據轉換爲無符號的8位數據。所以可以將兩個MMX寄存器不交錯的合爲一個64位數據。
③packsswb/packssdw
將32位-》16位,16位-》8位,都是有符號的數據。
2.3
運算指令
加法運算指令:paddb(w)(d):沒有越界保護的加法,當越界的時候僅僅丟棄超出範圍的高位比特,(b)(w)(d)分別爲8,16,32位加法;paddsb(w):具有越界保護的有符號加法,當上溢的時候爲0x7fff,下溢的時候爲0x8000;paddusb(w):具有越界保護的無符號加法,當上溢的時候爲0x7fff,下溢的時候爲0x0。
減法運算指令同上;add改爲sub。
乘法指令:pmullw / pmulhw
是4個16位數據的乘法,pmullw中是結果的低16位,pmulhw是結果的高16位。pmaddwd
乘加指令。
2.4
邏輯指令,移位指令和EMMS指令
細節參見《INTEL
體系結構MMX技術程序員參考手冊》。
III MMX經典處理策略
①數據輸入輸出:
在輸入數據的時候,經典的處理方法是將一個數組整個“Load”到MMX寄存器中。這樣簡單同時利用了MMX64位讀寫數據的能力,提高了性能。同樣在輸出的時候,也是將一個64位MMX寄存器中的數據內容整個“Store”到內存中。
如果實在是不能這樣處理的話,就要利用移位指令了。比如說將一個MMX內的4個16位數據分別拷貝到不同的內存變量(或者16位通用寄存器中)x1,x2,x3,x4,那麼可以這樣處理:
movdeax, mm1
psrlq mm1, 32
movd ebx, mm1
mov x1, ax
mov x2, bx
shr eax, 16
shr ebx, 16
mov x3, ax
mov x4, bx
可見如果不採用數組形式的話,輸入輸出將十分的麻煩。
②數據分組以及求絕對值的方法等:
細節請參閱《INTEL 體系結構MMX™ 技術開發者手冊》第五章
IV 自定義組合指令
①八位無符號數的移位:
在MMX指令集中是沒有8位數據的移位指令的,但是有的時候我們確實需要,所以可以用以下兩個指令來實現:
psrlq mm0,1
pand mm0,0x7f7f7f7f7f7f7f7f
②如何防止計算過程中越界:
比如在計算的時候,我們有(x1+x2+1)>>1,這個時候x1+x2就會越界(8位數據),那麼我們就不得不使用替代了辦法,比如(x1>>1+x2>>1)這個處理是不精確的,在不需要很精確的場合,是可以使用的,但是如果結果差錯1都不可容忍的話,就要進行一點處理:
pand mm0,0x01010101010101
// 保留數據的最後一位數
pand mm1,0x01010101010101
// 保留數據的最後一位數
por mm0,mm1
paddusb mmx,mm0
//修正數據
(x1>>2+x2>>2):這個處理是通用的
pand mm0,0x03030303030303
// 保留數據的最後兩位數
pand mm1, 0x03030303030303
// 保留數據的最後兩位數
paddusb mm0,mm1
psrlq mm0,2
pand mm0,0x3f3f3f3f3f3f3f3f
paddusb mmx,mm0
③符號擴展指令:
mm0:*,*,A,B =>
現在要符號擴展爲 mm0:(A符號)A, (A符號)B
movq mm1, mm0
pcgtm mm1, 0 //比較mm0,生成mm1:(A符號)
(B符號)()()
punpcklwd mm0, mm1
④分組指令
除了基本的分組指令以外,我們還可以利用移位指令和pand por指令來實現分組的功能,移位主要是要產生0,這樣por mm0,mm1就可以將mm0和mm1合併了。
比如:mm0(*,*,A,B) mm1(0,0,C,D)
則
psllq mm0,32
por mm0,mm1 => (A,B,C,D)
當然這個例子我們可以用普通的分組指令實現,但是在某些複雜的處理中,這樣的處理是必須的。
總之,要靈活運用MMX的現有指令來實現自己需要的功能。
V MMX編程心得
使用MMX技術進行編程,目的就是要提高運算速度,所以,對於如何儘可能的提高代碼的效率,我們是要特別關注的。這裏,我介紹一些需要注意的事項。
①
儘可能的提高內存訪問的容量,我們可以看看下面的代碼:
for (j=0; j<h; j++)
{
d[0] = s[0];
d[1] = s[1];
d[2] = s[2];
d[3] = s[3];
d[4] = s[4];
d[5] = s[5];
d[6] = s[6];
d[7] = s[7];
d[8] = s[8];
d[9] = s[9];
d[10] = s[10];
d[11] = s[11];
d[12] = s[12];
d[13] = s[13];
d[14] = s[14];
d[15] = s[15];
s+= lx2;
d+= lx;
}
__asm{
pushf
mov edx,dword ptr h
xor ecx,ecx
mov esi,dword ptr s
mov edi,dword ptr d
mov eax,lx2
mov ebx,lx
AGAIN:
movq mm0,byte ptr [esi]
movq mm1,byte ptr [esi+8]
movq byte ptr [edi],mm0
movq byte ptr [edi+8],mm1
add esi,eax
add edi,ebx
add ecx,1
cmp ecx,edx
jl AGAIN
emms
popf
}
僅僅將幾個8位的寫,改爲64位的寫,測試得到速度提升了25%,同樣的道理,我們要儘可能的將幾個movq寫在一起,這樣可以提高5%左右的速度。原C代碼的效率也是很高的,它不用數組的【】【】來尋址,而是將s+=
lx2; 從而將二維數組的尋址改爲一維數組的尋址。儘可能的減少尋址的複雜度,這也是一種高效的辦法。還有一點就是如果將原來的簡單賦值改爲memcpy()的話,可以提高大約10%的速度。這也是提高了數據流通容量的關係。
②
一些要注意的地方:
1.儘可能的使用static變量,
訪問這樣的變量是很快的=訪問立即數的速度
2.由於只有一個mmx移位寄存器, (移位分組指令)
是不能配對的
3.不要在eax使用完,
使用ax, 不要使用完一個mm1,就立即使用它
4.可以這樣立即使用mm1, movq mm2,mm1 movq mm1,mm3 (Z順序是可以的)
5.(4個以上)movq儘可能的在一起,
前提是在一起的mov不要使用一樣的mmx寄存器
6.mov eax, [esi] ([esi+2*eax])
訪問尋址的內存是特別的慢的
7.同上 stow
也是很慢的(mov cx,n; loop是很慢的,如果可能,要展開循環)
8.儘可能的在寄存器中完成操作,不要去訪問內存
9.用變量名訪問變量,尤其是static的,是很快.
10.訪問尋址的內存的速度下降》數據不對齊8位的速度下降》指令不配對的速度下降
11.所以在傳統的代碼優化的方法中,構造數組,然後將運算變爲查表的方法,有的時候在MMX技術內反而會降低速度。(這個時候,如果真的用查表有提升速度的話,建議採用段地址+偏移量的辦法)
|