很久以前,Heresy 曾經介紹過 OpenMP 這個超簡單的程式平行化的標準(參考);當時 Heresy 並沒有特別去管他的版本,不過實際上以 VisualStudio 來說,就算到了最新的 2013 版,也都還是只支援到 OpenMP 2.0(MSDN)。而實際上,OpenMP 在 2008 年就已經把標準更新到 3.0 了,而最新的版本,則是 2011 年的 3.1 版(維基百科)。
而在今年,OpenMP 則是發表了最新版的 OpenMP 4.0,做了相當大的改進!雖然現在似乎還沒有可以完整支援的編譯器(官方編譯器支援列表,不過似乎沒有更新的很快),不過這邊還是稍微來整理一下好了。
OpenMP 的官方網站是:http://www.openmp.org/,目前最新版的 OpenMP C++ API 文件也已經可以下載了(PDF),另外也有快速參考卡可以下載(PDF)。如果以 Intel 的《What will be new in OpenMP 4.0?》這份投影片(PDF)來看,OpenMP 4.0 最主要的新功能,基本上有五個:
- 使用者定義的 Reduction(User-defined Reductions)
- 支援 SIMD 的平行化(Support for SIMD parallelism)
- 強化 Task 的功能(Task Extensions)
- 支援加速裝置以及協同處理器(Support for Accelerators and Coprocessors)
- Thread-affinity Support
其中,Heresy 個人覺得最重要的,應該還是針對 SIMD 以及加速裝置的支援了!也就是說,以後如果開發環境有支援的話,就可以直接用 OpenMP 來做 GPGPU、用顯示卡、或是 Intel Xeon Phi 這類的計算卡來做大量平行化的加速了!
加速裝置
在以往的 OpenMP 來說,都是採用「shared memory」、所有的執行序都共用同一塊記憶體空間的配置,而從 OpenMP 4.0 開始,他已經可以在單台電腦上,去操作多張加速計算卡(顯卡、或是協同處理器)、讓他們使用各自獨立的記憶體空間,來做計算了~
透過新的 target 這個新的 directive,可以指定要使用哪個裝置來做計算、並配置相關的記憶體對應,之後就可以用指定的裝置來計算了~而再搭配 teams,也可以再去指定執行序的群組、來做更進一步的最佳化。在《OpenMP for Accelerators》這份投影片(PDF)裡面,有提供了一些簡單的範例:
double B[N] = ...; #pragma omp target device(0) map(tofrom:B) #pragma omp parallel for for( i = 0; i < N; ++ i ) B[i] += sin( B[i] );
在上面的程式碼裡面,除了本來用來把迴圈平行化的 omp parallel for 外,這邊還加入了一行新的、以 target 為首的 directive,這邊就是用來指定要使用第一個 device、並且把 B 這個變數對應到這個 device 上。而根據投影片的說法,這樣理論上是可以在 Intel Xeon Phi 這樣的協同處理器上運作的~
而如果是在顯示卡這種有做核心群組(例如 CUDA 的 thread block、nVIDIA GPU 架構內的 SMX)的話,則還可以透過 teams 來針對硬體的配置來做最佳化;修改後的程式可能會像下面這樣:
double B[N] = ...; #pragma omp target device(0) map(tofrom:B) #pragma omp teams num_teams(num_blocks) num_threads(bsize) #pragma omp distribute for( i = 0; i < N; i += num_blocks ) #pragma omp parallel for for( b = i; b < i + num_blocks; ++ b ) B[b] += sin( B[b] );
理論上,這樣在 nVIDIA 的 GPU 上,應該會有更好的效能。
這樣的程式雖然看來比本來的 OpenMP 複雜了些,但是相較於直接去寫 nVIDIA CUDA、或是 OpenCL 程式比起來,算是相當簡單的了!基本上,這種 GPGPU 的程式開發方法,應該也是當初 OpenACC 想做的;如果之後主流開發環境都能支援 OpenMP 4.0 的話,應該就不需要特別去找支援 OpenACC 的編譯器了。
SIMD
除了可以使用顯示卡等計算卡來做計算外,Heresy 認為另一個最重要的新功能,則是他可以透過 SIMD(Single instruction, multiple data)來做平行化(或者應該說是向量化?);使用 SIMD 的形式來做計算,主要是在同一個時間處理迴圈裡的多組資料,基本上是可以透過處理器的特殊指令,來做加速的~理論上這樣在符合條件的情況下,也會有相當程度的效能增進的~
要使用的話,最基本的使用方法也相當簡單,只要在 for 迴圈前加上
#pragma omp simd
就可以了。這樣的好處,是不必去特別研究 SIMD 的指令怎麼用,就可以簡單地把程式裡的迴圈向量化了。
而 SIMD 的向量化處理也可以和本來的平行化並用,只要寫成:
#pragma omp parallel for simd
這樣就好了~如此一來,程式除了會把迴圈做向量化處理外,也會建立多個執行序、來做平行化的計算。
另外,透過新的 directive,也可以指定自定義的函式可以在 SIMD 迴圈內呼叫,不過這邊就先不細講了。
至於其他像是可以自定義 reduction 的計算方法,或是在 task 方面的諸多強化,其實在需要時也都是相當有用的~不過由於現在 Heresy 手邊沒有可以拿來玩的編譯器,所以就先不介紹了。
而實際上,就算上面的 device 和 SIMD,也都僅只是看著文件寫的粗淺介紹,等到有可以玩的編譯器後,Heresy 應該會再認真玩看看、然後再來做分享的。
參考:
附註:
[…] OpenMP 4.0 以後的 GPGPU […]
讚讚
[…] 而實際上,OpenMP 4.0 開始,也有支援 SIMD 和 GPGPU 了~不過 Visual Studio 在這塊始終還是要加油就是了(參考)。 […]
讚讚
[…] 後來也有持續推出新版本,像是在 2013 年,就有推出支援 GPGPU 和 SIMD 的 OpenMP 4.0 了;在 2019,也推出了 5.0 版(可以參考《What’s new in OpenMP […]
讚讚
[…] SIMD 是 OpenMP 4.0 加入的功能,基本上就是把迴圈向量化、使用 CPU […]
讚讚
[…] Clang/LLVM 來開發的,它支援 C11、C++ 11、C++14、OpenMP 4.0 等標準語法,而且不管是 CPU 或 GPU […]
讚讚
open 4.0好像只有部份的功能能用,我在win 7使用MINGW-W64 gcc-4.9.2版的,居然omp_get_num_devices()=0 , GPU不能用!!!!
讚讚
是 OpenMP 4.0,不是 open 4.0 喔~
另外,OpenMP 只是一個規格,各家編譯器的實作程度都不一定,尤其是 GPU 加速更是傳統編譯器沒有的功能,所以支援的會更慢。
gcc 的話,應該是要參考這篇:
https://gcc.gnu.org/wiki/Offloading
讚讚
您好,這幾個月常常拜讀您的文章,
我本身是學統計的,最近被老闆要求要將其研究的算法平行化,
三個月前才開始學C,雖然成功地將老闆使用的R語言寫成C語言,
但是在平行化上面卻遇到了困難,之前嘗試過cuda,但失敗了,
所以老闆改希望我用openmp。
想請教您的是,有沒有什麼推薦的書去學習cuda或是openmp呢?
我們算法上需要平行的部分是想要讓各個執行緒上擁有一個至數個向量各自作運算。
P.S. 剛剛連到您的facebook才發現您以前也讀過中央數學,
我是95級的,或許該稱呼您一聲學長?XD
讚讚
NVIDIA 的 CUDA 基本上技術門檻相對比較高,如果沒有相當的 C/C++ 程式開發經驗,要直接上手應該有難度。
而 OpenMP 這類的方案,應該會是目前要做平行化最簡易的方案。
理論上只要你能把程式寫成獨立的 for 迴圈,就可以直接套上 OpenMP 來平行化。
但是,上述的方法,基本上都是假設每個執行序的計算方法是相同的、只有資料不一樣。
如果你每個執行序要做的計算不同的話,就比較不建議使用這兩者來做平行化,而直接使用 thread 的函式庫來做。
相關書籍的話,抱歉沒有研究了。
—
95…感覺好遠… orz
讚讚
[…] OpenMP 4.0 啊?都過了這麼久,Visual Studio 2015 還是死守在 OpenMP 2.0、連 3.0 […]
讚讚