MMX開發文檔

sdhttp://dev.gameres.com/Program/Other/MMXDev.htmMMX開發文檔


I  MMX
簡介

  IntelMMX™技術是對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 拷貝指令

movq64位數據拷貝,如果內存8位對齊的話,是一個64位寫,否則232位寫。

movd32位數據拷貝,注意:如果從內存向MMX寄存器拷貝,MMX32位清零!

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 運算指令

加法運算指令:paddbw)(d):沒有越界保護的加法,當越界的時候僅僅丟棄超出範圍的高位比特,(b)(w)(d)分別爲81632位加法;paddsbw):具有越界保護的有符號加法,當上溢的時候爲0x7fff,下溢的時候爲0x8000paddusbw):具有越界保護的無符號加法,當上溢的時候爲0x7fff,下溢的時候爲0x0

減法運算指令同上;add改爲sub

乘法指令:pmullw  / pmulhw 416位數據的乘法,pmullw中是結果的低16位,pmulhw是結果的高16位。pmaddwd 乘加指令。

2.4 邏輯指令,移位指令和EMMS指令

細節參見《INTEL 體系結構MMX技術程序員參考手冊》。

III  MMX經典處理策略

①數據輸入輸出:

在輸入數據的時候,經典的處理方法是將一個數組整個“Load”到MMX寄存器中。這樣簡單同時利用了MMX64位讀寫數據的能力,提高了性能。同樣在輸出的時候,也是將一個64MMX寄存器中的數據內容整個“Store”到內存中。

如果實在是不能這樣處理的話,就要利用移位指令了。比如說將一個MMX內的416位數據分別拷貝到不同的內存變量(或者16位通用寄存器中)x1x2x3x4,那麼可以這樣處理:

  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

②如何防止計算過程中越界:

比如在計算的時候,我們有(x1x21>>1,這個時候x1x2就會越界(8位數據),那麼我們就不得不使用替代了辦法,比如(x1>>1x2>>1)這個處理是不精確的,在不需要很精確的場合,是可以使用的,但是如果結果差錯1都不可容忍的話,就要進行一點處理:

pand        mm0,0x01010101010101             // 保留數據的最後一位數

pand        mm1,0x01010101010101             // 保留數據的最後一位數

  por         mm0,mm1                                 

paddusb     mmx,mm0                          //修正數據

x1>>2x2>>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 mm0mm1就可以將mm0mm1合併了。

比如: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 cxn;   loop是很慢的,如果可能,要展開循環)

8.儘可能的在寄存器中完成操作,不要去訪問內存

9.用變量名訪問變量,尤其是static,是很快.

10.訪問尋址的內存的速度下降數據不對齊8位的速度下降指令不配對的速度下降

11.所以在傳統的代碼優化的方法中,構造數組,然後將運算變爲查表的方法,有的時候在MMX技術內反而會降低速度。(這個時候,如果真的用查表有提升速度的話,建議採用段地址+偏移量的辦法)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章