OpenMP4.0: #pragma openmp simd实现SIMD指令优化(ARM,X86,MIPS)
考慮一下,CPU一般都是32或64位的寄存器,一次處理的數(shù)據(jù)長(zhǎng)度達(dá)到32或64位,對(duì)于圖像處理來(lái)說(shuō),一般是每個(gè)像素以8位為單位,那么我們?cè)趯?duì)一幅圖像每個(gè)像素做處理時(shí),用32位或64位的寄存器來(lái)處理8位的數(shù)據(jù),其實(shí)就是一性能上的浪費(fèi)。有沒(méi)有辦法充分利用CPU 32/64位的處理能能力,讓CPU一次處理多個(gè)8位數(shù)據(jù)呢?這就是本文要說(shuō)的SIMD.
向量化( Vectorization)
向量化( Vectorization)是一種單指令多數(shù)據(jù)( Single Instruction Mutiple Data,簡(jiǎn)稱SIMD)的并行執(zhí)行方式。具體而言,向量化是指相同指令在硬件向量處理單元( Vector Processing Unit簡(jiǎn)稱VPU)上對(duì)多個(gè)數(shù)據(jù)流進(jìn)行操作。這些硬件向量處理單元也被稱為SIMD單元。
例如,兩個(gè)向量的加法形成的第三個(gè)向量就是一個(gè)典型的SMD操作。許多處理器具有可同時(shí)執(zhí)行2、4、8或更多的SIMD(矢量)單元執(zhí)行相同的操作。
它通過(guò)循環(huán)展開(kāi)、數(shù)據(jù)依賴分析、指令重排等方式充分挖掘程序中的并行性,將程序中可以并行化的部分合成處理器支持的向量指令,通過(guò)復(fù)制多個(gè)操作數(shù)并把它們直接打包在寄存器中,從而完成在同一時(shí)間內(nèi)采用同步方式對(duì)多個(gè)數(shù)據(jù)執(zhí)行同一條指令,有效地提高程序性能。
還以前面圖像處理的應(yīng)用場(chǎng)景為例,向量化( Vectorization)可以允許一條SIMD指令一次實(shí)現(xiàn)多個(gè)8位像素的運(yùn)算處理。以intel CPU的SSE指令為例,SSE的寄存器達(dá)到128bit寬度,一次可以實(shí)現(xiàn)16個(gè)byte的算術(shù)運(yùn)算。(SSE是Intelr SIMD指令集,進(jìn)一步,還有升級(jí)版的AVX 256bit,和AVX512)??上攵?#xff0c;在不增加硬件設(shè)備投入的前提下,SIMD對(duì)于密集運(yùn)算程序的性能會(huì)帶來(lái)數(shù)倍乃至數(shù)十倍的提升。所以向量化可以充分挖掘處理器并行處理能力,非常適合于處理并行程度高的程序代碼.
不同的CPU體系的有不同的SIMD指令集標(biāo)準(zhǔn),比如:
Intel有的x86體系有SSE以及后續(xù)的升級(jí)版的AVX,AVX2,AVX512 等(參見(jiàn)《英特爾?流式 simd 擴(kuò)展技術(shù)》).
arm 平臺(tái)也有自己的SIMD指令集,叫NEON(參見(jiàn)《NEON》).
mips體系的SIMD指令集叫MSA(參見(jiàn)《MIPS SIMD》).
看到這里估計(jì)你該頭痛了,SIMD好是好,但這么多互不兼容SIMD指令標(biāo)準(zhǔn)。實(shí)際開(kāi)發(fā)中該怎么用呢?
向量化的實(shí)現(xiàn)通常可采用兩種方式:自動(dòng)向量化和手動(dòng)向量化.
手動(dòng)向量化
通過(guò)內(nèi)嵌手工編寫的匯編代碼或目標(biāo)處理器的內(nèi)部函數(shù)來(lái)添加SIMD指令從而實(shí)現(xiàn)代碼的向量化。
說(shuō)白了,就是開(kāi)發(fā)者要手工編寫匯編程序使用CPU的SIMD指令來(lái)實(shí)現(xiàn)向量化( Vectorization)。這要求開(kāi)發(fā)者具備很高的底層匯編開(kāi)發(fā)能力,這個(gè)過(guò)程對(duì)于開(kāi)發(fā)者而言痛苦而低效。而且只能針對(duì)特定平臺(tái)編寫程序,代碼不能跨平臺(tái)使用,總之代價(jià)很高,吃力不討好。
自動(dòng)向量化
編譯器通過(guò)分析程序中控制流和數(shù)據(jù)流的特征,識(shí)別并選出可以向量化執(zhí)行的代碼,并將標(biāo)量指令自動(dòng)轉(zhuǎn)換為相應(yīng)的SMD指令的過(guò)程。
也就是說(shuō),向量化的過(guò)程由編譯器自動(dòng)完成,開(kāi)發(fā)者只要編寫正常的C代碼就好,編譯器會(huì)自動(dòng)分析代碼結(jié)構(gòu),將適合向量化的C代碼部分自動(dòng)生成SIMD指令的向量化代碼。而且這些C代碼可以跨平臺(tái)編譯,針對(duì)不同的平臺(tái)生成不同的SIMD指令。開(kāi)發(fā)者不需要詳細(xì)了解SIMD指令的用法。也不需要具備匯編程序的編寫能力。
2013年, OpenMP4.0提供了預(yù)處理指令simd對(duì)函數(shù)和循環(huán)進(jìn)行向量化?,F(xiàn)在主流編譯器都支持了OpenMP4.0(比如gnu,intel Compiler,參見(jiàn) https://www.openmp.org/resources/openmp-compilers-tools/)。感謝OpenMP4.0,為SIMD指令的跨平臺(tái)應(yīng)用提供了可能。
OpenMP又是啥?
按照Wiki的解釋,OpenMP(Open Multi-Processing)是一套支持跨平臺(tái)共享內(nèi)存方式的多線程并發(fā)的編程API,使用C,C++和Fortran語(yǔ)言,可以在大多數(shù)的處理器體系和操作系統(tǒng)中運(yùn)行,包括Solaris, AIX, HP-UX, GNU/Linux, Mac OS X, 和Microsoft Windows。包括一套編譯器指令、庫(kù)和一些能夠影響運(yùn)行行為的環(huán)境變量。參見(jiàn)(https://zh.wikipedia.org/wiki/OpenMP)
OpenMP早期是用來(lái)實(shí)現(xiàn)跨平臺(tái)的多線程并發(fā)編程的一套標(biāo)準(zhǔn)。到了OpenMP4.0加入了對(duì)SIMD指令的支持,以實(shí)現(xiàn)跨平臺(tái)的向量化支持。
那么如何使用OpenMP來(lái)實(shí)現(xiàn)SIMD指令優(yōu)化呢(向量化)呢?簡(jiǎn)單說(shuō)只要在代碼的循環(huán)邏輯前加入#pragma omp simd預(yù)處理指令就可以,不需要任何依賴庫(kù)。簡(jiǎn)單吧?
#pragma omp simd指令應(yīng)用于代碼中的循環(huán)邏輯,可以讓多個(gè)迭代的循環(huán)利用simd指令實(shí)現(xiàn)并發(fā)執(zhí)行。
示例
多說(shuō)無(wú)益,還是舉個(gè)栗子吧!
下面就是一個(gè)簡(jiǎn)單BGRA轉(zhuǎn)RGB圖像的程序,沒(méi)有什么復(fù)雜的邏輯,就是把4字節(jié)BGRA格式像素轉(zhuǎn)為3字節(jié)的RGB格式像素。與普通的C程序沒(méi)有任何不同,只是在for循環(huán)前面多了一個(gè)#pragma omp simd預(yù)處理指令。
這個(gè)預(yù)處理令告訴編譯器下面這個(gè)循環(huán)要使用SIMD指令來(lái)實(shí)現(xiàn)向量化。
test_simd.c
/** test_simd.c** Created on: Nov 27, 2018* Author: gyd*/ #if 1 void bgra2rgb(const char *src,char*dst,int w,int h) { #pragma omp simdfor(int y=0;y<h;++y){for(int x=0;x<w;++x){dst[(y*w+x)*3 ] = src[(y*w+x)*4 + 2];dst[(y*w+x)*3+1] = src[(y*w+x)*4 + 1];dst[(y*w+x)*3+2] = src[(y*w+x)*4 + 0];}} }int main() {char bgra_mat[480*640*4];char rgb_mat[480*640*3];bgra2rgb(bgra_mat,rgb_mat,480,640);} #endif程序部分就這樣了,只是多了一行預(yù)處理指令而已,夠簡(jiǎn)單吧。重要的是代碼的編譯方式,以gcc編譯器為例,下面是命令行編譯test_simd.c的過(guò)程:
$ gcc -O3 -fopt-info -fopenmp -mavx2 -o test_simd test_simd.c test_simd.c:13:3: note: loop vectorized test_simd.c:13:3: note: loop versioned for vectorization because of possible aliasing上面編譯命令執(zhí)行時(shí)輸出test_simd.c:13:3: note: loop vectorized,就顯示line 13的循環(huán)代碼已經(jīng)實(shí)現(xiàn)了循環(huán)向量化.下面詳細(xì)解釋幾個(gè)特別的編譯選項(xiàng)的意義:
-
-fopenmp 打開(kāi)OpenMP預(yù)處理指令支持開(kāi)關(guān),使用此選項(xiàng),代碼中的#pragma omp simd預(yù)處理指令才有效。
參見(jiàn) https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html#C-Dialect-Options -
-mavx2 指定使用intel AVX2指令集。如果目標(biāo)CPU不支持AVX,也可以根據(jù)目標(biāo)CPU的類型改為低版本的-msse4.1 -msse4.2 -msse4 -mavx
參見(jiàn) https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html#Option-Summary -
-fopt-info 顯示優(yōu)化過(guò)程的輸出,該選項(xiàng)只是用于輸出顯示,指示哪些代碼已經(jīng)被優(yōu)化了,可以不用,就沒(méi)有上面的輸出顯示。
參見(jiàn) https://gcc.gnu.org/onlinedocs/gcc/Developer-Options.html#Developer-Options -
-O3 3級(jí)優(yōu)化選項(xiàng),參見(jiàn) https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Optimize-Options
對(duì)于mips平臺(tái),編譯方式是這樣的,與x86平臺(tái)唯一的不同就是-mavx2改為-mmsa(參見(jiàn) 《Option-Summary》):
$ mips-linux-gnu-gcc -O3 -fopt-info -fopenmp -mmsa -o test_simd_msa test_simd.c test_simd.c:13:3: note: loop vectorized test_simd.c:13:3: note: loop versioned for vectorization because of possible aliasing如果是arm平臺(tái),編譯方式應(yīng)該是這樣的(我還沒(méi)有試過(guò)),參見(jiàn)參考資料5,6:
arm-none-linux-gnueabi-gcc -mfpu=neon -ftree-vectorize -ftree-vectorizer-verbose=1 -c test_simd.c驗(yàn)證
如何驗(yàn)證代碼是SIMD指令實(shí)現(xiàn)的呢?
最直接的辦法 就是查看生成的可執(zhí)行文件的反匯編代碼。
可以用gdb打開(kāi)生成的可執(zhí)行文件test_simd,通過(guò)查看生成的指令來(lái)驗(yàn)證是否對(duì)循環(huán)實(shí)現(xiàn)了向量化優(yōu)化。
執(zhí)行g(shù)db test_simd打開(kāi)gdb,再執(zhí)行disassemble /m bgra2rgb顯示bgra2rgb函數(shù)的匯編代碼,翻幾頁(yè)就可以看到類似vmovdqa 0x52f(%rip),%ymm11這樣的指令,像vmovdqa這種v開(kāi)頭的指令就是AVX2的SIMD指令。代表SIMD指令已經(jīng)被用于程序中
如果你不習(xí)慣用命令行的gdb工具,也可以用eclipse來(lái)查看反匯編代碼,如下,在程序中加個(gè)斷點(diǎn),調(diào)試執(zhí)行到指定的斷點(diǎn),在Disassembly窗口就可以查看到對(duì)應(yīng)的匯編代碼
總結(jié)
上面的例子非常簡(jiǎn)單,說(shuō)明#pragma omp simd預(yù)處理指令的強(qiáng)大,但這并不是全部,也并不是表面看的那么簡(jiǎn)單,#pragma omp simd不是萬(wàn)能的,一段循環(huán)代碼是不是能被向量化,有不少的限制條件。并不是所有的循環(huán)都可以直接用#pragma omp simd來(lái)向量化優(yōu)化。關(guān)于#pragma omp simd更詳細(xì)的說(shuō)明請(qǐng)參見(jiàn)參考資料2,3。如果你覺(jué)得英文看得吃力,建議找本書(shū)翻翻,系統(tǒng)化的資料比網(wǎng)上零散的文章看起來(lái)更有效率,比如這本《多核異構(gòu)并行計(jì)算(OpenMP4.5C\C++篇)》 ,我也是前幾天從京東買的,寫得一般,不夠通俗,但這樣的系統(tǒng)化中文書(shū)籍本身就不多,也只有它了,看看就成。
參考資料:
1.《#pragma omp simd - IBM》
2.《PDF:SIMD Vectorization with OpenMP》
3.《Options Controlling C Dialect.》
4. 《GCC Developer Options》
5. 《ARM NEON Development》
6. 《1.4.3. Automatic vectorization》
7. 《OpenMP in Visual C++》
總結(jié)
以上是生活随笔為你收集整理的OpenMP4.0: #pragma openmp simd实现SIMD指令优化(ARM,X86,MIPS)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: smtp服务器地址
- 下一篇: hrbusrt 1900 权限问题