日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

OpenMP4.0: #pragma openmp simd实现SIMD指令优化(ARM,X86,MIPS)

發(fā)布時(shí)間:2023/12/14 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OpenMP4.0: #pragma openmp simd实现SIMD指令优化(ARM,X86,MIPS) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

考慮一下,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)被用于程序中

$ gdb test_simdGNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1Copyright (C) 2016 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "x86_64-linux-gnu".Type "show configuration" for configuration details.For bug reporting instructions, please see:<http://www.gnu.org/software/gdb/bugs/>.Find the GDB manual and other documentation resources online at:<http://www.gnu.org/software/gdb/documentation/>.For help, type "help".Type "apropos word" to search for commands related to "word".(gdb) disassemble /m bgra2rgbDump of assembler code for function bgra2rgb:0x0000000000400660 <+0>: test %ecx,%ecx0x0000000000400662 <+2>: jle 0x400a1a <bgra2rgb+954>0x0000000000400668 <+8>: lea 0x8(%rsp),%r100x000000000040066d <+13>: and $0xffffffffffffffe0,%rsp0x0000000000400671 <+17>: lea 0x0(,%rdx,4),%eax0x0000000000400678 <+24>: xor %r11d,%r11d0x000000000040067b <+27>: pushq -0x8(%r10)0x000000000040067f <+31>: push %rbp0x0000000000400680 <+32>: mov %rsp,%rbp0x0000000000400683 <+35>: push %r150x0000000000400685 <+37>: push %r140x0000000000400687 <+39>: push %r130x0000000000400689 <+41>: push %r120x000000000040068b <+43>: xor %r13d,%r13d0x000000000040068e <+46>: push %r100x0000000000400690 <+48>: push %rbx0x0000000000400691 <+49>: xor %r10d,%r10d0x0000000000400694 <+52>: xor %ebx,%ebx0x0000000000400696 <+54>: mov %eax,-0x34(%rbp)0x0000000000400699 <+57>: lea (%rdx,%rdx,2),%eax0x000000000040069c <+60>: vmovdqa 0x41c(%rip),%ymm8 # 0x400ac00x00000000004006a4 <+68>: mov %eax,-0x38(%rbp)---Type <return> to continue, or q <return> to quit---0x00000000004006a7 <+71>: mov %edx,%eax0x00000000004006a9 <+73>: lea (%rax,%rax,2),%r150x00000000004006ad <+77>: shl $0x2,%rax0x00000000004006b1 <+81>: mov %rax,-0x40(%rbp)0x00000000004006b5 <+85>: lea -0x21(%rdx),%eax0x00000000004006b8 <+88>: shr $0x5,%eax0x00000000004006bb <+91>: add $0x1,%eax0x00000000004006be <+94>: mov %eax,-0x54(%rbp)0x00000000004006c1 <+97>: shl $0x5,%eax0x00000000004006c4 <+100>: mov %eax,-0x48(%rbp)0x00000000004006c7 <+103>: lea -0x1(%rdx),%eax0x00000000004006ca <+106>: mov %eax,-0x44(%rbp)0x00000000004006cd <+109>: lea (%rax,%rax,2),%rax0x00000000004006d1 <+113>: mov %rax,-0x50(%rbp)0x00000000004006d5 <+117>: nopl (%rax)0x00000000004006d8 <+120>: test %edx,%edx0x00000000004006da <+122>: jle 0x4009ac <bgra2rgb+844>0x00000000004006e0 <+128>: movslq %r11d,%r90x00000000004006e3 <+131>: movslq %ebx,%r120x00000000004006e6 <+134>: lea (%rdi,%r9,1),%r80x00000000004006ea <+138>: add -0x40(%rbp),%r90x00000000004006ee <+142>: lea (%rsi,%r12,1),%rax0x00000000004006f2 <+146>: add %rdi,%r9---Type <return> to continue, or q <return> to quit---0x00000000004006f5 <+149>: cmp %r9,%rax0x00000000004006f8 <+152>: lea (%r15,%r12,1),%r90x00000000004006fc <+156>: setae %r14b0x0000000000400700 <+160>: add %rsi,%r90x0000000000400703 <+163>: cmp %r9,%r80x0000000000400706 <+166>: setae %r9b0x000000000040070a <+170>: or %r9b,%r14b0x000000000040070d <+173>: je 0x4009e0 <bgra2rgb+896>0x0000000000400713 <+179>: cmp $0x1f,%edx0x0000000000400716 <+182>: jbe 0x4009e0 <bgra2rgb+896>0x000000000040071c <+188>: xor %r9d,%r9d0x000000000040071f <+191>: cmpl $0x1f,-0x44(%rbp)0x0000000000400723 <+195>: jbe 0x40095c <bgra2rgb+764>0x0000000000400729 <+201>: vmovdqa 0x52f(%rip),%ymm11 # 0x400c600x0000000000400731 <+209>: vmovdqa 0x547(%rip),%ymm10 # 0x400c800x0000000000400739 <+217>: vmovdqa 0x55f(%rip),%ymm9 # 0x400ca00x0000000000400741 <+225>: vmovdqa 0x577(%rip),%ymm7 # 0x400cc00x0000000000400749 <+233>: vmovdqa 0x58f(%rip),%ymm6 # 0x400ce00x0000000000400751 <+241>: vmovdqa 0x5a7(%rip),%ymm5 # 0x400d000x0000000000400759 <+249>: vmovdqa 0x5bf(%rip),%ymm4 # 0x400d200x0000000000400761 <+257>: vmovdqu (%r8),%xmm10x0000000000400766 <+262>: add $0x1,%r9d0x000000000040076a <+266>: sub $0xffffffffffffff80,%r8---Type <return> to continue, or q <return> to quit---0x000000000040076e <+270>: add $0x60,%rax0x0000000000400772 <+274>: vmovdqu -0x60(%r8),%xmm130x0000000000400778 <+280>: vinserti128 $0x1,-0x70(%r8),%ymm1,%ymm10x000000000040077f <+287>: vmovdqu -0x40(%r8),%xmm30x0000000000400785 <+293>: vinserti128 $0x1,-0x50(%r8),%ymm13,%ymm130x000000000040078c <+300>: vmovdqu -0x20(%r8),%xmm120x0000000000400792 <+306>: vinserti128 $0x1,-0x30(%r8),%ymm3,%ymm30x0000000000400799 <+313>: vinserti128 $0x1,-0x10(%r8),%ymm12,%ymm120x00000000004007a0 <+320>: vpand %ymm13,%ymm8,%ymm20x00000000004007a5 <+325>: vpsrlw $0x8,%ymm13,%ymm130x00000000004007ab <+331>: vpand %ymm1,%ymm8,%ymm00x00000000004007af <+335>: vpsrlw $0x8,%ymm1,%ymm10x00000000004007b4 <+340>: vpackuswb %ymm13,%ymm1,%ymm130x00000000004007b9 <+345>: vpand %ymm12,%ymm8,%ymm140x00000000004007be <+350>: vpsrlw $0x8,%ymm12,%ymm10x00000000004007c4 <+356>: vpackuswb %ymm2,%ymm0,%ymm00x00000000004007c8 <+360>: vpand %ymm3,%ymm8,%ymm20x00000000004007cc <+364>: vpsrlw $0x8,%ymm3,%ymm30x00000000004007d1 <+369>: vpackuswb %ymm1,%ymm3,%ymm10x00000000004007d5 <+373>: vpermq $0xd8,%ymm13,%ymm130x00000000004007db <+379>: vpackuswb %ymm14,%ymm2,%ymm140x00000000004007e0 <+384>: vpermq $0xd8,%ymm1,%ymm10x00000000004007e6 <+390>: vpand %ymm13,%ymm8,%ymm3---Type <return> to continue, or q <return> to quit---0x00000000004007eb <+395>: vpermq $0xd8,%ymm0,%ymm00x00000000004007f1 <+401>: vpermq $0xd8,%ymm14,%ymm140x00000000004007f7 <+407>: vpand %ymm1,%ymm8,%ymm10x00000000004007fb <+411>: vpand %ymm0,%ymm8,%ymm20x00000000004007ff <+415>: vpsrlw $0x8,%ymm0,%ymm00x0000000000400804 <+420>: vpand %ymm14,%ymm8,%ymm150x0000000000400809 <+425>: vpsrlw $0x8,%ymm14,%ymm140x000000000040080f <+431>: vpackuswb %ymm1,%ymm3,%ymm10x0000000000400813 <+435>: vpackuswb %ymm14,%ymm0,%ymm00x0000000000400818 <+440>: vpackuswb %ymm15,%ymm2,%ymm20x000000000040081d <+445>: vmovdqa 0x41b(%rip),%ymm15 # 0x400c400x0000000000400825 <+453>: vpermq $0xd8,%ymm1,%ymm10x000000000040082b <+459>: vpermq $0xd8,%ymm0,%ymm00x0000000000400831 <+465>: vpermq $0xd8,%ymm2,%ymm20x0000000000400837 <+471>: vpshufb 0x2c0(%rip),%ymm1,%ymm12 # 0x400b000x0000000000400840 <+480>: vpshufb 0x297(%rip),%ymm0,%ymm3 # 0x400ae00x0000000000400849 <+489>: vpermq $0x4e,%ymm12,%ymm130x000000000040084f <+495>: vpermq $0x4e,%ymm3,%ymm140x0000000000400855 <+501>: vpshufb 0x2e2(%rip),%ymm1,%ymm12 # 0x400b400x000000000040085e <+510>: vpshufb 0x2b9(%rip),%ymm0,%ymm3 # 0x400b2---Type <return> to continue, or q <return> to quit---

如果你不習(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)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。