Linux上編譯SIMD指令程式

來源:互聯網
上載者:User

Linux上編譯SIMD指令程式

 

(1) g++編譯時間加入 __MMX__,  __SSE__ predefinitions;

(2) gcc的連結時-march=pentium4 -mmmx -msse -m3dnow

(3) 包括標頭檔: xintrinsic.h

如果用Eclipse開發時注意設定的地方。

 

IA-32 Intel體繫結構的指令主要分為以下幾類:

  • 通用
  • x87 FPU
  • MMX技術
  • SSE/SSE2/SSE3擴充

MMX/SSE類擴充引入了SIMD(單指令多資料)的執行模式,可用於加速多媒體應用。 下面簡要介紹一下這些指令的執行環境和特徵。

  • 8個32位通用寄存器可為各個SIMD擴充所使用;
  • MMX:8個64位MMX寄存器(mm0 - mm7),也可為各SSE擴充所使用;
    • 資料為整數,最多支援兩個32位
    • 運算中沒有寄存器能夠進行溢出指示
  • SSE:8個128位xmm寄存器,MXSCR寄存器,EFLAGS寄存器
    • 支援單精確度浮點
    • MXSCR含有rounding, overflow標誌
    • 支援64位SIMD整數
  • SSE2:執行環境同sse
    • 雙精確度浮點
    • 128位整數
    • 雙—單精確度轉換
  • SSE3:與Inte Prescott處理器一同發布不久,共13條指令
    • 主要增強了視頻解碼、3D圖形最佳化和超執行緒效能

MMX技術出現最早,目前幾乎所有的X86處理器都提供支援,包括嵌入式X86, 所以下面的討論主要基於MMX,但方法完全適用於SSEn, 包括像AMD的3D Now等其它SIMD擴充。

MMX指令又分為以下幾種:

  • 資料傳送:movd, movq
  • 資料轉換:packsswb, packssdw, packuswb, punpckhbw, punpckhwd, punpckhdq, punpcklbw, punpcklwd, punpckldq
  • 並 行算術:paddb, paddw, paddd, paddsb, paddsw, paddusb, paddusw, psubb, psubw, psubd, psubsb, psubsw, psubusb, psubusb, psubusw, pmulhw, pmullw, pmaddwd
  • 並行比較:pcmpeqb, pcmpeqw, pcmpeqd, pcmpgtb, pcmpgtw, pcmpgtd
  • 並行邏輯:pand, pandn, por, pxor
  • 移位與旋轉:psllw, pslld, psllq, psrlw, psrld, psrlq, psraw, psrad
  • 狀態管理:emms

這些指令除了需要注意功能外,還需要注意處理的資料類型。以上內容為背景介紹,細節請參考手冊。

效能最佳化 ...Performance Optimization

當 使用C/C++完成了一個嵌入式應用的所有功能,效能問題常擺在面前, 這時可以使用profile工具(如gprof)找出產生瓶頸的函數, 將這些函數使用彙編徹底重寫, 例如MPEG-4轉碼器xvid項目 [4]就使用了這種方法, 而且針對不同處理器/指令集分別給出了不同的最佳化, 正是如此該項目無論功能、還是效能均為一流, 顯然這是深度最佳化的目標所在。

在使用流水線、VLIW以及SIMD的 體繫結構(比如某些DSP)上, 整個函數的手工最佳化可以帶來幾倍到幾十倍的效能提升。 不過,效能允許,對於函數內關鍵區段使用一些特定的實現, 既突出重點提高效能,又可以盡多地利用C/C++的進階特徵, 相對縮短開發週期。 下面給出使用GCC時,應用MMX指令的幾種混合編程方法:

  • Intel C/C++ 編譯器intrinsics
  • GCC builtin操作
  • 嵌入彙編asm construct

Intel C/C++ 編譯器intrinsics ...Intel C/C++ Compiler Intrinsics

查看IA-32 Intel指令集手冊時, 部分指令的解釋中會有一項“Intel C/C++ Compiler Intrinsic Equivalent”, 會指出該指令對等的intrinsic。 intrinsic在C/C++程式中的文法是以函數形式出現, 編譯時間可以直接翻譯為一條MMX指令(複合情況會產生最直接的幾條), 換言之,如果不使用intrinsic,可能需要多條C/C++陳述式完成, 而編譯器卻並不能保證將這幾條語句能夠產生這條最高效的MMX指令。 並不是每條MMX指令都有對等的intrinsic, 手冊的附錄中列出了所有的, 它們分為簡單型(simple)和複合型(composite)兩種, 每個簡單型的就是對應一條指令,而複合型則對應多條指令。

GCC支援Intel C/C++ Compiler Intrinsics。用法如下樣本:

      #include <stdio.h>
#include <xmmintrin.h> /*一定需要包括此標頭檔*/
/*gcc -Wall -march=pentium4 -mmmx -o ins mmx_ins.c*/
int main(int argc,char *argv[])
{
/*使用MMX做以下向量的點積*/
short in1[] = {1, 2, 3, 4};
short in2[] = {2, 3, 4, 5};
int out1;
int out2;
__m64 m1; /* MMX支援64位整數的mm寄存器 */
__m64 m2; /* MMX操作需要使用mm寄存器 */
__m128 m128; /* for SSEn only*/
/*每次往mm寄存器裝入兩個short型的數,注意是兩個*/
m1 = _mm_cvtsi32_si64(((int*)in1)[0]);
m2 = _mm_cvtsi32_si64(((int*)in2)[0]);
/*一條指令進行4個16位整數的乘加*/
/*產生兩個32位整數*/
m2 = _mm_madd_pi16(m1, m2);
/*將低32位整數放入通用寄存器*/
out1 = _mm_cvtsi64_si32(m2);
/*將高32位整數右移後,放入通用寄存器*/
m2 = _mm_slli_pi32(m2, 32);
out2 = _mm_cvtsi64_si32(m2);
/*清除MMX狀態*/
_mm_empty();
/*將兩個32位元相加,結果為8*/
out1 += out2;
printf("a: %d"n", out1);
return(0);
}

幾點說明:

  • 即使你不是P4平台,編譯時間也請使用以下選項,
    /*gcc -Wall -march=pentium4 -mmmx -o ins mmx_ins.c*/
    否則,會出現如下類似資訊:
    ...xmmintrin.h:34:3: #error "SSE instruction set not enabled"
  • 最 終結果實際並沒有求得四對乘積的和,只是前兩對的, instrinsic _mm_cvtsi32_si64隻向mm寄存器放入了低32位,高32位為零, 但mmx有指令movq可以做到64位的資料傳送,intrinsic沒有對應, 這也說明並不是所有的指令有等價的intrinsic。
  • 當計算的向量為兩對0x8000, 0x8000時,即(-2^15)*(-2^15) + (-2^15)*(-2^15) , 結果應該為 2^31,但計算出來的值是 -2^31, 因為發生了溢出,可程式無從知道。 這是使用MMX時,應特別注意的,計算溢出沒有任何標誌位指示,一個極大的值變為極小,SSE對此做了改善。
  • 程式不再使用MMX之時,注意使用emms指令清除MMX狀態。

使用built-in操作 ...GCC built-in Operation

什麼是built-in操作?就是對待MMX運算元,就如int, float等基礎資料型別 (Elementary Data Type)一般, 有相應定義的操作,如加(+)、減(-),或者資料類型之間的轉換。 詳細內容參考GNU GCC Manual [5] Extensions to the C Language Family4#4Built-in Functions4#4 X86 Built-in Functions一節。

一些MMX指令有其相應的built-in操作, 下面一段代碼為例:

      #include <stdio.h>
/*無需特別的標頭檔,built-in嘛*/
/* gcc -Wall -o bins builtinmmx.c*/
/*定義了一個vector資料類型,hi表示16位,4表示4個*/
typedef int v4hi __attribute__ ((mode(V4HI)));
/*定義了2個32位的vector類型,si表示32位*/
typedef int v2si __attribute__ ((mode(V2SI)));
int main(int argc,char *argv[])
{
short pa[4] = {0x8000, 0x8000, 1, -1};
short pb[4] = {0x8000, 0x7FFF, -1, -2};

v4hi va, vb;
v4hi vsum;

va = ((v4hi*)pa)[0];
vb = ((v4hi*)pb)[0];

/* 4個16位進行飽和加 */
//vsum = __builtin_ia32_paddsw(va, vb);
/* 4個16位還可以直接進行加法,但不同於兩個long long相加 */
vsum = va + vb;

/*vector的輸出還需要強制轉換為long long*/
printf("...with MMX instructions...to compute vec_add: %llx "n", (long long)vsum);

//結果1:0xfffd0000ffff8000
//結果2:0xfffd0000ffff0000

return(0);
}

幾點說明:

  • 是的,這裡built-in vector及其操作,隨著GCC的發展正在加強。如果需要使用以上範例,應使用GCC 3.4以上版本;
  • 使用builtin函數時,與intrinsic相似;但本質卻是不同,這裡兩個向量使用‘+’操作就說明了vector也如其它資料類型一樣,編譯器直接支援,只不過這裡的加法就是指四個單元數分別相加,低位單元的進位不會影響相鄰高位單元的資料;
  • vector還可以強制轉換為通用資料。



回頁首

嵌入彙編 ...Inline asm

GCC一開始就允許C代碼中嵌入asm指令,並不只是針對MMX指令, 不過對於MMX技術,顯然也是一個很好的利用方法, 詳細的文法請參考GNU GCC手冊 [5], 或者GCC: The Complete Reference [6]''Inline Assembly''一節。 如下是一個點積的例子:

      #include <stdio.h>
/** GCC -o ins inlinemmx.c **/
int main(int argc,char *argv[])
{
int i;
int result;
short a[] = {1, 2, 3, 4, 5, 6, 7, 8};
short b[] = {1, 1, 1, 1, 1, 1, 1, 1};
printf("...with MMX instructions..."n");

/*首先,將點積合累積寄存器清零,實際預設就為0?*/
asm("pandn %%mm5,%%mm5;"::);
/*讀入a, b,每四對數相乘後分兩組相加,形成兩組和*/
/*這裡的迴圈控制是C在做*/
for(i = 0; i < sizeof(a)/sizeof(short); i += 4){
asm("movq %0,%%mm0;"
movq %1,%%mm1;"
pmaddwd %%mm1,%%mm0;"
paddd %%mm0,%%mm5; #相乘後相加 "
:
: "m" (a[i]), "m" (b[i]));
}
/*將兩組和分離,並相加*/
asm("movq %%mm5, %%mm0;"
psrlq $32,%%mm5;"
paddd %%mm0, %%mm5;"
movd %%mm5,%0;"
emms"
:"=r" (result)
:);
printf("result: 0x%x"n", result);
//這裡結果為0x24
return(0);
}

幾點說明:

  • 這裡是典型的在函數中C和彙編混合編程;
  • 注意彙編指令中運算元的順序;
  • 這裡可以直接使用movq等沒有intrinsics/built-in對應的指令;
  • 注意在asm指令序列中間不要加雜注釋,可能導致產生的程式碼不正確。

MMX實用一例:合成濾波器 ...Synthesis Filter in X86 SIMD INSTRUCTIONS

下面是合成濾波器(Synthesis Filter)的一個最佳化過程, 合成濾波器在語音編解碼中有廣泛應用, 運行時也佔用了整個演算法中較高比例的時間。

      for (i = 0; i < lg; i++)
{
s = L_mult(x[i], a[0]);/*L_mult是相乘後左移*/
for (j = 1; j <= M; j++){/*M這裡固定為10*/
s = L_msu(s, a[j], yy[-j]);/*L_msu是乘減後左移操作*/
}

s = L_shl(s, 3); /*左移三位*/
*yy++ = g729round(s);
}
#endif

 

上面的代碼,因為記憶體迴圈為10,可以考慮展開,並統一操作為乘加指令。

      /*為了使用乘加操作,需要調整10個係數的順序*/
for(i = 0; i < M; i++)
ta[i] = -a[M - i];
ta[11] = 0;
ta[10] = a[0];
for (i = 0; i < lg; i++){
*yy = x[i];
yy[1] = 0;
s = L_mac(s, ta[11], yy[1]);
s = L_mac(s, ta[10], yy[0]);
s = L_mac(s, ta[9], yy[-1]);
s = L_mac(s, ta[8], yy[-2]);
s = L_mac(s, ta[7], yy[-3]);
s = L_mac(s, ta[6], yy[-4]);
s = L_mac(s, ta[5], yy[-5]);
s = L_mac(s, ta[4], yy[-6]);
s = L_mac(s, ta[3], yy[-7]);
s = L_mac(s, ta[2], yy[-8]);
s = L_mac(s, ta[1], yy[-9]);
s = L_mac(s, ta[0], yy[-10]);

s = L_shl(s, 3);
*yy++ = g729round(s);
}

以上迴圈核心正好可以將MMX的8個寄存器全部利用。

      /*為了使用乘加操作,需要調整10個係數的順序*/
for(i = 0; i < M; i++)
ta[i] = -a[M - i];
ta[11] = 0;
ta[10] = a[0];
/*11個係數分別放入3個MMX寄存器,0作填充*/
asm("movq %0,%%mm0;"
movq %1,%%mm1;"
movq %2,%%mm2""
:"
: "m" (ta[0]), "m" (ta[4]), "m"(ta[8]));

/*利用MMX技術進行濾波器核心操作*/
for (i = 0; i < lg; i++){
*yy = x[i];
yy[1] = 0;
asm("pandn %%mm6,%%mm6;"
movq %1,%%mm3;"
movq %2,%%mm4;"
movq %3,%%mm5;"
pmaddwd %%mm0,%%mm3;"
pmaddwd %%mm1,%%mm4;"
pmaddwd %%mm2,%%mm5;"
paddd %%mm3, %%mm6;"
paddd %%mm4, %%mm6;"
paddd %%mm5, %%mm6;"
movq %%mm6, %%mm7;"
psrlq $32, %%mm6;"
paddd %%mm7, %%mm6;"
movd %%mm6,%0;"
emms"
:
:"r"(s), "m" (yy[-10]), "m" (yy[-6]), "m"(yy[-2]));
/*因為指令結果飽和屬性的限制,s還沒有左移,所以下面多做一位飽和左移*/
s = L_shl(s, 4);
*yy++ = g729round(s);
}

幾點說明:

  • 注意:以上嵌入的彙編代碼輸出結果s放在了輸入處,屬於實踐中的個案;
  • MMX沒有乘左移之類的DSP指令,甚至還沒有加飽和之類的操作,SSE中有一定增強;
  • 以上操作,理論上存在溢出可能,所以最後使用原有的飽和左移操作,減少了一定風險;
  • 上面的部分代碼操作顯然允許並行,這在VLIW系統中十分有用;
  • 這已經形成了該濾波器全面最佳化的核心。

總結 ...Conclusion

如果願意盡多地利用SIMD技術,可能需要更多地使用彙編級的編碼, 不過也有一些進階語言和彙編的混合編程技術能夠協助你, 它們有的提高效能更大一些, 有的形式上更優雅些,本質上效率也不錯, 都不失好的方法,建議嘗試。

正是如此,一方面CPU上支援越來越多的SIMD指令集擴充, 另一方面GCC也正在加緊支援這些擴充的易用,對,正在, 碰到一些問題,先想辦法繞過去, 這裡使用GCC 3.4.1,根據經驗效果還是不錯的。

關於文檔

GCC中SIMD指令的應用方法

This document was generated using the LaTeX2HTML translator Version 2002 (1.62)

Copyright 1993, 1994, 1995, 1996, Nikos Drakos, Computer Based Learning Unit, University of Leeds.
Copyright , 1998, 1999, Ross Moore, Mathematics Department, Macquarie University, Sydney.

The command line arguments were: latex2html -iso_language CN -html_version 4.0,unicode -address '2004 CoreUp Designs' -local_icons -split 0 -nonavigation gccsimd

The translation was initiated by on 2004-12-13

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.