乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      漫談并發(fā)編程:用MPI進(jìn)行分布式內(nèi)存編程(入門篇)

       映月hfu8s42n59 2018-10-10

      0x00 前言

      本篇是MPI的入門教程,主要是為了簡單地了解MPI的設(shè)計(jì)和基本用法,方便和現(xiàn)在的Hadoop、Spark做對(duì)比,并嘗試?yán)斫馑鼈冎g在設(shè)計(jì)上有什么區(qū)別。

      身處Hadoop、Spark這些優(yōu)秀的分布式開發(fā)框架蓬勃發(fā)展的今天,老的分布式編程模型是否沒有必要學(xué)習(xí)?這個(gè)很難回答,但是我更傾向于花一個(gè)下午的時(shí)候來學(xué)習(xí)和了解它。

      關(guān)于并發(fā)和并行編程系列的文章請(qǐng)參考文章集合

      文章結(jié)構(gòu)

      1. 舉個(gè)最簡單的例子,通過這個(gè)例子讓大家對(duì)MPI有一個(gè)基本的理解。
      2. 解釋一些和MPI相關(guān)的概念。
      3. 列舉一些MPI的常用函數(shù),以及基本用法
      4. 通過兩個(gè)例子詳細(xì)說明MPI的用法

      0x01 舉個(gè)栗子

      安裝

      建議在Ubuntu上安裝,不過筆者嘗試一下,報(bào)了各種錯(cuò)。正好Win10可以安裝一個(gè)Linux的bash,就安裝了一下,用起來和原生Linux沒什么區(qū)別,挺方便。

      一句搞定。

      sudo apt-get install libcr-dev mpich2 mpich2-doc
      • 1

      helloworld

      MPI的c語言版helloworld。這是一個(gè)最簡單的版本,相當(dāng)于是每個(gè)進(jìn)程都打印一下helloworld。

      該例子中的一些方法以及概念在后面都會(huì)解釋,而且會(huì)有兩個(gè)比這個(gè)功能更全一點(diǎn)的例子來幫助理解。

      #include <mpi.h>
      #include <stdio.h>
      
      int main (int argc, char* argv[])
      {
        int rank, size;
      
        MPI_Init (&argc, &argv);      /* starts MPI*/
        MPI_Comm_rank (MPI_COMM_WORLD, &rank);        /* get current process id*/
        MPI_Comm_size (MPI_COMM_WORLD, &size);        /* get number of processes*/
        printf( "Hello world from process %d of %d\n", rank, size );
        MPI_Finalize();
        return 0;
      }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14

      運(yùn)行

      先編譯,如果有for循環(huán)的話,記得加上后面的參數(shù)。

      mpicc mpi_hello.c -o hello -std=c99
      • 1

      再運(yùn)行:

      $ mpirun -np 5 ./hello
      Hello world from process 0 of 5
      Hello world from process 1 of 5
      Hello world from process 2 of 5
      Hello world from process 3 of 5
      Hello world from process 4 of 5
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

      總結(jié)

      從上面的簡單例子可以看出 一個(gè)MPI程序的框架結(jié)構(gòu)可以用下圖表示 把握了其結(jié)構(gòu)之后,下面的主要任務(wù)就是掌握MPI提供的各種通信方法與手段。

      安裝時(shí)遇到的問題

      來一個(gè)我在Ubuntu16.04下遇到的錯(cuò)誤,實(shí)在不想解決這些亂七八糟的,就跳過了。

      $sudo apt-get install libcr-dev mpich2 mpich2-doc
      Reading package lists... Done
      Building dependency tree       
      Reading state information... Done
      Package mpich2 is not available, but is referred to by another package.
      This may mean that the package is missing, has been obsoleted, or
      is only available from another source
      However the following packages replace it:
        mpich:i386 mpich
      
      Package mpich2-doc is not available, but is referred to by another package.
      This may mean that the package is missing, has been obsoleted, or
      is only available from another source
      However the following packages replace it:
        mpich-doc
      
      E: Package 'mpich2' has no installation candidate
      E: Package 'mpich2-doc' has no installation candidate
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19

      0x02 基本概念

      什么是MPI

      對(duì)MPI的定義是多種多樣的,但不外乎下面三個(gè)方面,它們限定了MPI的內(nèi)涵和外延:

      • MPI 是一個(gè)庫,不是一門語言。MPI 提供庫函數(shù)/過程供 C/C++/FORTRAN 調(diào)用。
      • MPI 是一種標(biāo)準(zhǔn)或規(guī)范的代表,而不特指某一個(gè)對(duì)它的具體實(shí)現(xiàn)。
      • MPI 是一種消息傳遞編程模型。最終目的是服務(wù)于進(jìn)程間通信這一目標(biāo) 。

      名詞和概念

      程序代碼:

      這里的程序不是指以文件形式存在的源代碼、可執(zhí)行代碼等,而是指為了完成一個(gè)計(jì)算任務(wù)而進(jìn)行的一次運(yùn)行過程。

      進(jìn)程(Process)

      一個(gè) MPI 并行程序由一組運(yùn)行在相同或不同計(jì)算機(jī) /計(jì)算節(jié)點(diǎn)上的進(jìn)程或線程構(gòu)成。為統(tǒng)一起見,我們將 MPI 程序中一個(gè)獨(dú)立參與通信的個(gè)體稱為一個(gè)進(jìn)程。

      進(jìn)程組:

      一個(gè) MPI程序的全部進(jìn)程集合的一個(gè)有序子集。進(jìn)程組中每個(gè)進(jìn)程都被賦予一個(gè)在改組中唯一的序號(hào)(rank),用于在該組中標(biāo)識(shí)該進(jìn)程。序號(hào)范圍從 0 到進(jìn)程數(shù)-1。

      通信器(communicator):

      有時(shí)也譯成通信子,是完成進(jìn)程間通信的基本環(huán)境,它描述了一組可以互相通信的進(jìn)程以及它們之間的聯(lián)接關(guān)系等信息。MPI所有通信必須在某個(gè)通信器中進(jìn)行。通信器分域內(nèi)通信器(intracommunicator)和域間通信器(intercommunicator)兩類,前者用于同一進(jìn)程中進(jìn)程間的通信,后者則用于分屬不同進(jìn)程的進(jìn)程間的通信。

      MPI 系統(tǒng)在一個(gè) MPI 程序運(yùn)行時(shí)會(huì)自動(dòng)創(chuàng)建兩個(gè)通信器:一個(gè)稱為 MPI_COMM_WORLD,它包含 MPI 程序中所有進(jìn)程,另一個(gè)稱為MPI_COMM_SELF,它指單個(gè)進(jìn)程自己所構(gòu)成的通信器。

      序號(hào)(rank):

      即進(jìn)程的標(biāo)識(shí),是用來在一個(gè)進(jìn)程組或一個(gè)通信器中標(biāo)識(shí)一個(gè)進(jìn)程。MPI 的進(jìn)程由進(jìn)程組/序號(hào)或通信器/序號(hào)唯一確定。

      消息(message):

      MPI 程序中在進(jìn)程間傳遞的數(shù)據(jù)。它由通信器、源地址、目的地址、消息標(biāo)簽和數(shù)據(jù)構(gòu)成。

      通信(communication):

      通信是指在進(jìn)程之間進(jìn)行消息的收發(fā)、同步等操作。

      0x02 MPI核心接口

      用過Hadoop的童鞋應(yīng)該都記得經(jīng)典的Map和Reduce接口,我們?cè)趯慚R程序的時(shí)候主要就在寫自己實(shí)現(xiàn)的Map和Reduce方法。

      MPI比Hadoop需要關(guān)注的稍微多一點(diǎn)點(diǎn)。

      注意: 這幾個(gè)核心的接口還是要了解一下的。暫時(shí)可以看一眼跳過去,后面在看程序的時(shí)候回過頭多對(duì)比一下就能記住了。

      我們簡單地理解一下這6個(gè)接口,其實(shí)可以分為3類:
      1. 開始和結(jié)束MPI的接口:MPI_InitMPI_Finalize
      2. 獲取進(jìn)程狀態(tài)的接口:MPI_Comm_rank、MPI_Comm_size
      3. 傳輸數(shù)據(jù)的接口:MPI_Send、MPI_Recv

      關(guān)于傳輸數(shù)據(jù)的接口,可以看下圖的理解。

      1. MPI_Init(&argc, &argv)

      初始化MPI執(zhí)行環(huán)境,建立多個(gè)MPI進(jìn)程之間的聯(lián)系,為后續(xù)通信做準(zhǔn)備。

      2. MPI_Comm_rank(communicator, &myid)

      用來標(biāo)識(shí)各個(gè)MPI進(jìn)程的,給出調(diào)用該函數(shù)的進(jìn)程的進(jìn)程號(hào),返回整型的錯(cuò)誤值。兩個(gè)參數(shù):MPI_Comm類型的通信域,標(biāo)識(shí)參與計(jì)算的MPI進(jìn)程組; &rank返回調(diào)用進(jìn)程中的標(biāo)識(shí)號(hào)。

      3. MPI_Comm_size(communicator, &numprocs)

      用來標(biāo)識(shí)相應(yīng)進(jìn)程組中有多少個(gè)進(jìn)程。

      4. MPI_Finalize()

      結(jié)束MPI執(zhí)行環(huán)境。

      5. MPI_Send(buf,counter,datatype,dest,tag,comm)

      • buf:發(fā)送緩沖區(qū)的起始地址,可以是數(shù)組或結(jié)構(gòu)指針;
      • count:非負(fù)整數(shù),發(fā)送的數(shù)據(jù)個(gè)數(shù);
      • datatype:發(fā)送數(shù)據(jù)的數(shù)據(jù)類型;
      • dest:整型,目的的進(jìn)程號(hào);
      • tag:整型,消息標(biāo)志;comm:MPI進(jìn)程組所在的通信域

      含義:向通信域中的dest進(jìn)程發(fā)送數(shù)據(jù),數(shù)據(jù)存放在buf中,類型是datatype,個(gè)數(shù)是count,這個(gè)消息的標(biāo)志是tag,用以和本進(jìn)程向同一目的進(jìn)程發(fā)送的其它消息區(qū)別開來。

      6. MPI_Recv(buf,count,datatype,source,tag,comm,status)

      • source:整型,接收數(shù)據(jù)的來源,即發(fā)送數(shù)據(jù)進(jìn)程的進(jìn)程號(hào);
      • status:MPI_Status結(jié)構(gòu)指針,返回狀態(tài)信息。

      0x03 進(jìn)階版HelloWorld

      在這里舉第二個(gè)例子——一個(gè)進(jìn)階版的HelloWorld。不再像第一個(gè)例子那樣簡單地打印HelloWorld,在這個(gè)程序中,我們指派其中一個(gè)進(jìn)程復(fù)雜輸出,其它的進(jìn)程向他發(fā)送要打印的消息。

      程序

      在這個(gè)程序中,為了方便理解我會(huì)注釋大部分的代碼。

      注意注釋。

      
      #include <stdio.h>
      #include <string.h>  /* For strlen             */
      //MPI相關(guān)的庫
      #include <mpi.h>     /* For MPI functions, etc */
      
      const int MAX_STRING = 100;
      
      int main(void) {
         char       greeting[MAX_STRING];  /* String storing message*/
         int        comm_sz;               //進(jìn)程數(shù)
         int        my_rank;               //當(dāng)前進(jìn)程的進(jìn)程號(hào)
      
         //初始化MPI
         MPI_Init(NULL, NULL);
      
         //獲取進(jìn)程的數(shù)量,并存入comm_sz變量中
         MPI_Comm_size(MPI_COMM_WORLD, &comm_sz);
      
         //獲取當(dāng)前進(jìn)程的進(jìn)程號(hào)
         MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
      
         // 進(jìn)程號(hào)不為0的處理邏輯。
         // 在該程序中,進(jìn)程號(hào)不為0的進(jìn)程,只負(fù)責(zé)發(fā)數(shù)據(jù)給進(jìn)程0。
         if (my_rank != 0) {
            //創(chuàng)建要發(fā)送的數(shù)據(jù)
            sprintf(greeting, "Greetings from process %d of %d!",
                  my_rank, comm_sz);
            //發(fā)送數(shù)據(jù)給進(jìn)程0
            MPI_Send(greeting, strlen(greeting)+1, MPI_CHAR, 0, 0,
                  MPI_COMM_WORLD);
         }
         // 進(jìn)程號(hào)為0的處理邏輯
         else {  
            // 打印進(jìn)程0的數(shù)據(jù)
            printf("Hello! Greetings from process %d of %d!\n", my_rank, comm_sz);
            // 循環(huán)接收其它進(jìn)程發(fā)送的數(shù)據(jù),并打印。
            for (int q = 1; q < comm_sz; q++) {
               // 接收其它進(jìn)程的數(shù)據(jù)
               MPI_Recv(greeting, MAX_STRING, MPI_CHAR, q,
                  0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
               // 打印
               printf("%s\n", greeting);
            }
         }
      
         // 關(guān)閉MPI
         MPI_Finalize();
      
         return 0;
      }  /* main */
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52

      運(yùn)行

      看一下運(yùn)行結(jié)果。

      第一條是我們的進(jìn)程0打印的,其余的4條都是接收其它進(jìn)程的數(shù)據(jù)。

      $ mpicc mpi_hello.c -o hello  -std=c99
      $ mpirun -np 5 ./hello
      Hello! Greetings from process 0 of 5!
      Greetings from process 1 of 5!
      Greetings from process 2 of 5!
      Greetings from process 3 of 5!
      Greetings from process 4 of 5!
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

      0x04 MPI實(shí)現(xiàn)梯形積分法

      問題描述

      用梯形積分法來估計(jì)函數(shù) y=f(x) 的圖像中,兩條垂直線與x軸之間的區(qū)域大小。即下圖(1)中陰影部分面積。

      解法

      如上圖中的(b),基本思想就是,將x軸的區(qū)間劃分為n個(gè)等長的子區(qū)間,然后估計(jì)每個(gè)子區(qū)間范圍內(nèi)的圖形面積。最后相加即可。(當(dāng)然了,這是估算的面積)

      其中,梯形的面積如下:

      梯形面積= h/2 * (f(xi) + f(xi+1))
      • 1

      其中高h(yuǎn)是我們等分的一個(gè)區(qū)間值,h=(b-a)/n。

      那么整個(gè)圖形的面積如下:

      串行程序

      根據(jù)上面的公式,我們可以得到一個(gè)串行版的程序:

      h = (b-a)/n
      approx = (f(a) + f(b)) /2.0
      for(i=1;i<n-1;i++):
          x_i = a + i*h
          approx += f(x_i)
      approx = h*approx
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

      MPI程序

      整個(gè)MPI程序設(shè)計(jì)如下:

      • 進(jìn)程1~n, 負(fù)責(zé)各自的矩形面積
      • 進(jìn)程0,負(fù)責(zé)將所有矩形面積加起來求和

      如下圖

      對(duì)應(yīng)到代碼如下:

      int main(void) {
         int my_rank, comm_sz, n = 1024, local_n;   
         double a = 0.0, b = 3.0, h, local_a, local_b;
         double local_int, total_int;
         int source;
      
         MPI_Init(NULL, NULL);
         MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
         MPI_Comm_size(MPI_COMM_WORLD, &comm_sz);
      
         // 區(qū)間大小,所有進(jìn)程一樣
         h = (b-a)/n;        
         local_n = n/comm_sz;  // 每個(gè)processor需要處理的梯形的數(shù)目
      
         /* Length of each process' interval of
          * integration = local_n*h.  So my interval
          * starts at: */
         local_a = a + my_rank*local_n*h;
         local_b = local_a + local_n*h;
         local_int = Trap(local_a, local_b, local_n, h);
      
         // 將所有的進(jìn)程的結(jié)果相加
         if (my_rank != 0) {
            MPI_Send(&local_int, 1, MPI_DOUBLE, 0, 0,
                  MPI_COMM_WORLD);
         }
         // 每個(gè)進(jìn)程單獨(dú)計(jì)算梯形面積
         else {
            total_int = local_int;
            for (source = 1; source < comm_sz; source++) {
               MPI_Recv(&local_int, 1, MPI_DOUBLE, source, 0,
                  MPI_COMM_WORLD, MPI_STATUS_IGNORE);
               total_int += local_int;
            }
         }
      
         // 進(jìn)程0打印結(jié)果
         if (my_rank == 0) {
            printf("With n = %d trapezoids, our estimate\n", n);
            printf("of the integral from %f to %f = %.15e\n",
                a, b, total_int);
         }
         MPI_Finalize();
      
         return 0;
      } /*  main  */
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46

      Trap是梯形積分法的串行實(shí)現(xiàn)。供每一個(gè)processor調(diào)用。

      double Trap(
            double left_endpt  /* in */,
            double right_endpt /* in */,
            int    trap_count  /* in */,
            double base_len    /* in */) {
         double estimate, x;
         int i;
      
         estimate = (f(left_endpt) + f(right_endpt))/2.0;
         for (i = 1; i <= trap_count-1; i++) {
            x = left_endpt + i*base_len;
            estimate += f(x);
         }
         estimate = estimate*base_len;
      
         return estimate;
      }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17

      結(jié)果

      $mpirun -np 6 ./trap
      With n = 1024 trapezoids, our estimate
      of the integral from 0.000000 to 3.000000 = 8.894946975633502e+00
      
      • 1
      • 2
      • 3
      • 4

      0xFF 總結(jié)

      趁著端午假期的一個(gè)下午,把MPI做了一個(gè)小的總結(jié)。 程度不深,主要是了解MPI的一些基本特性。

      暫時(shí)總結(jié)到這里,后續(xù)的工作和學(xué)習(xí)中如果再遇到了和MPI相關(guān)的知識(shí)點(diǎn),再繼續(xù)深入。

      完整代碼請(qǐng)看github地址。

      參考


      作者:dantezhao |簡書 | CSDN | GITHUB

      個(gè)人主頁:http://
      文章可以轉(zhuǎn)載, 但必須以超鏈接形式標(biāo)明文章原始出處和作者信息

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多