平行運算簡介 / 實例操作企鵝也會的 MPICH 研究員 : 鄧偉華 wade@nchc.org.tw
什麼是平行計算 傳統 : 單一程序 單一 CPU
什麼是平行計算 ( 續 ) 平行計算 程序切割 多 CPUs
為什麼要平行計算 簡省時間 解決大型問題 即時性 使用更多來自網路上的資源 使用大量 便宜 PCs 取代超級電腦 記憶體不足
平行計算種類 Flynn's taxonomy 多處理器架構 單一資料 Single Data 多資料 Multiple Data 單一程序 Single Instruction SISD SIMD 多程序 Multiple Instruction MISD MIMD
Single Instruction, Single Data (SISD) 非平行計算 單一程序 單一資料 ex : a = 1 + 1 1 1 a = 1 + 1
Single Instruction, Multiple Data(SIMD) 平行計算單一程序多資料 ex : a = b + 1, a, b 是二組陣列, 則同一時間就可以計算完成 而不需要算完 a[0] = b[0] + 1, 再算 a[1] = b[1] + 1 再算 a[n] = b[n] + 1 b[0] 1 b[1] 1 b[n] 1 a = b + 1
Single Instruction, Multiple Data(MISD) 平行計算多程序單一資料 a = (2 + 3) + (2-3) + (2 * 3) + (2 / 3) 2 3 2 + 3 2-3 2 * 3 2 / 3 + a
Multiple Instruction, Multiple Data(MIMD) 平行計算多程序多資料 a = (b + c) + (d - e) + (f * g), a, b, c, d, e, f, g 為陣列如果有三臺機器可分別執行 (b + c) (d - e) 及 (f * g), 則三個運算可同時進行 b[1] c[1] d[1] e[1] f[1] g[1] b[n] c[n] d[n] e[n] f[n] g[n] b + c d - e + a f * g
如何執行 MPICH 平行程式 啟動 MPD mpdboot -n 4 -f machine_file -n how many mpds to start -f hostsfile 列出所有 nodes mpdtrace 執行 mpiexec -n 12./mpi/a.out 結束 MPD mpdallexit
MPICH 設計注意事項 MPICH 屬於 SIMD 架構 指令及資料量切割 優 : 有助於平行運算 缺 : 增加傳輸次數及傳輸量
如何使用載入 / 啟動 MPICH #include <stdio.h> #include <mpi.h> 啟動該程式在多個 CPU 上的平行計算工作 main (int argc, char **argv) { 得知參與平行計算的 CPU 個數 (nproc) MPI_Init(&argc, &argv); MPI_Comm_size (MPI_COMM_WORLD, &numprocs); MPI_Comm_rank (MPI_COMM_WORLD, &myid);...... 得知我是第幾個 CPU (myid) from 0 MPI_Finalize(); return 0; } 結束平行計算工作 12
HELLO MPICH 目標 : 每個 node 將自己的 id 印出, 並且將所有的參與活動的 node 總數也印出, 顯示出自己的主機名稱
HELLO MPICH #include <stdio.h> #include <mpi.h> main (int argc, char **argv) { int rank, size, len; char name[mpi_max_processor_name]; MPI_Init(&argc, &argv); int myid, numprocs; /* 取得 node 總數 */ MPI_Comm_size(MPI_COMM_WORLD,&numprocs); /* 取得本身 node id / rank */ MPI_Comm_rank(MPI_COMM_WORLD,&myid); /* 取得本身 host name */ MPI_Get_processor_name(name, &len); printf("this is machine %d of %d name = %s\n, myid, numprocs, name); MPI_Finalize(); } }
結果
Point to point communication node-00 node-01
Point to point communication node-01 node-00 node-02 node-03 node-04
Point to point communication node-01 node-00 node-02 node-03 node-04
MPI_Send MPI_Send ((void *)&data, icount, DATA_TYPE, idest, itag, MPI_COMM_WORLD) data 要送出去的資料起點, 可以是純量 (scalar) 或陣列 (array) 資料 icount 要送出去的資料數量, 當 icount 的值大於一時, data 必須是陣列 DATA_TYPE 要送出去的資料類別, MPI 內定的資料類別 idest 是收受資料的 CPU id itag 要送出去的資料標籤 MPI_COMM_WORLD 通信域
MPI_Recv MPI_Recv( void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status ) buf 要接收的資料起點, 可以是純量 (scalar) 或陣列 (array) 資料 icount 要接收的資料數量, 當 icount 的值大於一時, data 必須是陣列 DATA_TYPE 要接收的資料類別, MPI 內定的資料類別 source 是收受資料的 CPU id itag 要接收的資料標籤 MPI_COMM_WORLD 通信域
範例 目標 : 每個 node 將自己的 id 資訊送給下一個 node node-01 00 node-02 01 node-01 02
#include <mpi.h> #include <stdio.h> main(int argc,char **argv) { int n, myrank, numprocs; MPI_Status status; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD,&numprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); /* node 0 will send the first message */ if(myrank == 0) { n = myrank; MPI_Send(&n, 1, MPI_INT, 1, 99, MPI_COMM_WORLD); printf("[ndde %d] %d >> [Node %d]\n\n", myrank, n, myrank+1); } /* node 1 to node n-2 will send message to the next node */ if(myrank>0 && myrank<numprocs-1) { MPI_Recv(&n, 1, MPI_INT, myrank-1, 99, MPI_COMM_WORLD, &status); printf("[node %d] << %d [Node %d]\n", myrank, n, status.mpi_source); n = myrank; MPI_Send(&n, 1, MPI_INT, myrank+1, 99, MPI_COMM_WORLD); printf("[ndde %d] %d >> [Node %d]\n\n", myrank, n, myrank+1); } /* the final node n-1 will not send any message but receive*/ if(myrank==numprocs-1) { MPI_Recv(&n, 1, MPI_INT, myrank-1, 99, MPI_COMM_WORLD, &status); printf("[node %d] << %d [Node %d]\n", myrank, n, status.mpi_source); } MPI_Finalize(); }
動手作 每個 node 將訊息傳送給 node 0, 由, node 0 統一印出 node-01 node-00 node-02 node-03 node-04
提示 main(int argc, char **argv){ /* Node 0 will do the following */ if(myrank == 0) { /* receive messages from other nodes */ MPI_Recv(); } /* other Nodes will do the following */ if(myrank!= 0) { /* send node's rank to Node 0 */ MPI_Send(message, 20, MPI_CHAR, 0, 99, MPI_COMM_WORLD); }
結果
再動手作 讓 node 0 可以接受來自任何 node 的訊息, 每個 node 將訊息標上不同 tag (0, 1, 2...) 後傳給 node 0 node-04 node-00 node-01
提示 MPI_ANY_SOURCE 接收來自任何 node 訊息 MPI_ANY_TAG 接收任何 tag
結果
Collective communication node-01 node-00 node-02 node-03 node-04
Collective communication node-01 node-00 + node-02 node-03 node-04
MPI_Reduce MPI_Reduce ( void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm ) sendbuf address of send buffer (choice) count number of elements in send buffer (integer) datatype data type of elements of send buffer (handle) op reduce operation (handle) root rank of root process (integer) comm communicator (handle)
計算 1 + 2 + 3 +... + 100 #include <stdio.h> #include <mpi.h> main (int argc, char **argv) { int rank, size, i; int myid, numprocs; int mytotal = 0; int Total = 0; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &numprocs); MPI_Comm_rank(MPI_COMM_WORLD, &myid); for(i = 1; i <= 100; i++) { if((i % numprocs) == myid) { mytotal += i; } MPI_Reduce(&myTotal, &Total, 1, MPI_INT, MPI_SUM, 0,MPI_COMM_WORLD); } if(myid==0) { printf("total = %d\n", Total); } MPI_Finalize(); }
結果
動手作 計算 Fibonacci number 從第 1 項加至第 n 項總合 Fibonacci number f(n) = f(n - 1) + f(n - 2) f(0) = 1, f(1) = 1 1, 1, 2, 3, 5, 8,...
參考 http://www-unix.mcs.anl.gov/mpi/mpich1/docs http://www-unix.mcs.anl.gov/mpi/www/www3/ http://www.mpi-forum.org/ https://trac.nchc.org.tw/grid/wiki/mpich