最近在學一門課,叫做“C++與並行計算”。要用到多CPU(進程)並行的原理,實現語言是C++的MPI接口。聯想到上學期用到CUDA C/C++來做並行計算,就對這兩門語言做一個總結,分享下自己關於並行計算的認識。
並行計算一般有兩個維度,一個是指令(Instruction)或程序(Program),另一個是數據(Data)。這樣,就可以歸納出各種並行模式(S代表Single,M代表Multiple)。
除了SISD,其他幾個都算是並行計算方法。這裡重點介紹下SPMD。
SPMD是最簡單的一種並行計算模式。SP意味著程序員只需寫一份代碼,MD意味著這些代碼對不同的數據應該分別處理。而並行,則要求數據處理的過程要同時進行。通俗的講,就是一份代碼被復制了多份,然後每份代碼單獨跑一份數據,從而實現並行。這就引出了一個問題:數據是如何存儲的?
數據的存儲可以分為兩大類:分布式存儲和共享內存。
分布式存儲意味著不同的進程/指令處理不同的數據,大家互相不干擾。基於多CPU的MPI並行計算接口用的就是這種思想。
共享內存則要求不同的進程/指令可以同時修改同一塊數據。這樣,進程之間的通信將變得簡單。缺點是容易造成數據讀寫沖突而需要謹慎對待。基於GPU的CUDA C/C++並行計算就用到了這種方法。
鑒於最近幾年個人計算機多CPU的興起,利用多個CPU來處理同一個任務不失為最簡單的並行計算方法。其中的代表方法就是MPI。MPI全稱是Message Passing Interface,是一個消息傳遞接口(或約定)。MPI的特點可以用以下幾點來概括:
1. MPI屬於SPMD框架;
2. 數據是分布式存儲的;
3. 把握進程之間消息的傳遞是寫好MPI程序的關鍵!
最簡單的MPI “Hello, World!”程序如下:(存儲為hello.c)
#include <stdio.h> #include <mpi.h> // MPI庫 int main(int argc, char *argv[]) { MPI_Init(&argc, &argv); // 啟動MPI並行計算 printf("Hello, World!\n"); MPI_Finalize(); // 結束MPI並行計算 return(0); }
MPI程序的編譯不同於一般的C/C++程序,需要用單獨的mpi命令編譯。對於C MPI程序,編譯命令是mpicc;對於C++ MPI程序,編譯命令則是mpigxx。例如編譯上面的C MPI程序:
$mpicc hello.c -o hello
同樣,運行C/C++ MPI程序需要用到mpi的運行命令:mpirun 或 mpiexec。最常用的格式如下:
$mpirun -np 3 hello
-np是可選的參數,表示啟動的進程數,默認為1。這裡啟動了三個進程,從而屏幕上將打印出三行"Hello, World!"。
GPU並行計算的崛起得益於大數據時代的到來,而傳統的多CPU並行計算已經遠遠不能滿足大數據的需求。GPU最大的特點是它擁有超多計算核心,往往成千上萬核。而每個核心都可以模擬一個CPU的計算功能,雖然單個GPU核心的計算能力一般低於CPU。
CUDA,全稱是Compute Unified Device Architecture,即統一計算架構,是由生產GPU最有名的英偉達公司提出的CPU+GPU混合編程框架。CUDA C/C++語言有如下特點:
1. 也是SPMD框架;
2. 兼有分布式存儲和共享內存的優點;
3. 把握GPU的帶寬是充分利用GPU計算資源的關鍵。
一般,經過一定優化的CUDA C/C++程序的計算速度相比於傳統的CPU程序的計算速度要快幾倍到幾十倍。正因為如此,在目前火熱的深度學習領域,越來越多的科研工作者和工程師都開始利用GPU和CUDA來並行加速。
並行計算,尤其是利用GPU和CUDA來並行加速是很誘人的一門技術。然而,從我個人的經驗來講,寫一個並行計算程序比寫一個串行程序難多了。難點主要體現在以下幾點:
1. 並行程序需要更長的代碼,從而增加了工作量;
2. 並行程序的各個進程執行進度不確定,增加了debug的困難。
3. 需要對硬件架構、內存有更高的把握。
不過,考慮到並行計算誘人的前景,這些難點還是值得克服的。至少,對於一個機器學習的科研工作者而言,幾十倍的速度將大大的減少用於做實驗的時間。所以,何樂而不為呢?
歡迎討論!
參考:
1. SPMD:http://en.wikipedia.org/wiki/SPMD
2. MPI:https://www.sharcnet.ca/help/index.php/Getting_Started_with_MPI