精华内容
下载资源
问答
  • 四种网络工作模式有:对等模式、客户/服务器模式以及专用服务器模式和浏览器/服务器模式。1、对等模式(P2P,peer-to-peer)是一种通信模式,其中每一方都拥有相同的功能,任何一方都可以启动通信会话。在某些情况下,...

    四种网络工作模式有:对等模式、客户/服务器模式以及专用服务器模式和浏览器/服务器模式。

    1、对等模式(P2P,peer-to-peer)是一种通信模式,其中每一方都拥有相同的功能,任何一方都可以启动通信会话。在某些情况下,对等模式通信是通过给每个节点服务器和客户端功能来进行的。

    2、客户/服务器模式即Client-Server(C/S)结构。C/S结构通常采取两层结构。服务器负责数据的管理,客户机负责完成与用户的交互任务。

    3、专用服务器模式特点和基于服务器模式功能差不多,只不过服务器在分工上更加明确。在大型网络中服务器可能要为用户提供不同的服务和功能。使用一台服务器可能承受不了这么大压力,所以,就需要有多台服务器为其用户提供服务,并且每台服务器提供专一的网络服务。

    4、B/S架构即浏览器和服务器架构模式,是随着Internet技术的兴起,对C/S架构的一种变化或者改进的架构。在这种架构下,用户工作界面是通过WWW浏览器来实现,极少部分事务逻辑在前端(Browser)实现,但是主要事务逻辑在服务器端(Server)实现,形成所谓三层3-tier结构。

    e27fc292c8553b374a3bfb1603a08afe.png

    扩展资料:

    客户/服务器模式的客户机通过局域网与服务器相连,接受用户的请求,并通过网络向服务器提出请求,对数据库进行操作。服务器接受客户机的请求,将数据提交给客户机,客户机将数据进行计算并将结果呈现给用户。

    服务器还要提供完善安全保护及对数据完整性的处理等操作,并允许多个客户机同时访问服务器,这就对服务器的硬件处理数据能力提出了很高的要求。

    展开全文
  • 【MPI学习2】MPI并行程序设计模式:对等模式 & 主从模式 转自:http://www.cnblogs.com/xbf9xbf/p/5154428.html 这里的内容主要是都志辉老师《高性能计算之并行编程技术——MPI并行程序设计》 书上有一些代码...

    【MPI学习2】MPI并行程序设计模式:对等模式 & 主从模式

    转自:http://www.cnblogs.com/xbf9xbf/p/5154428.html

    这里的内容主要是都志辉老师《高性能计算之并行编程技术——MPI并行程序设计》

    书上有一些代码是FORTAN的,我在学习的过程中,将其都转换成C的代码,便于统一记录。

     

    这章内容分为两个部分:MPI对等模式程序例子 & MPI主从模式程序例子

    1. 对等模式MPI程序设计

    1.1 问题背景

    这部分以Jacobi迭代为具体问题,列举了三个求解Jacobi迭代问题的MPI对等模式程序。

    这里需要阐明一下,书上的Jacobi迭代具体的背景可以参考这个内容:http://www.mcs.anl.gov/research/projects/mpi/tutorial/mpiexmpl/src/jacobi/C/main.html

    简答说,就是矩阵每个元素都是其上下左右四个元素的平均值,矩阵四个边界的值不变。

    这里学习的重点暂时放在如何完成上述矩阵的迭代的功能实现,不应该偏离过多去纠结Jacobi迭代,提高专注度。

    1.2 第一版最原始的Jacobi迭代对等程序

    代码如下:

    复制代码
      1 #include "mpi.h"
      2 #include <stdio.h>
      3 #include <stdlib.h>
      4 
      5 #define N 8 
      6 #define SIZE N/4
      7 #define T 2 
      8 
      9 
     10 void print_myRows(int, float [][N]);
     11 
     12 int main(int argc, char *argv[])
     13 {
     14     float myRows[SIZE+2][N], myRows2[SIZE+2][N];
     15     int myid;
     16     MPI_Status status;
     17 
     18     MPI_Init(&argc, &argv);
     19     MPI_Comm_rank(MPI_COMM_WORLD, &myid);
     20 
     21     int i,j;
     22     /*初始化*/
     23     for ( i = 0; i<SIZE+2; i++)
     24     {
     25         for ( j = 0; j<N; j++)
     26         {
     27             myRows[i][j] = myRows2[i][j] = 0;
     28         } 
     29     }
     30     if ( 0 == myid) {
     31         for ( j = 0; j<N; j++)
     32             myRows[1][j] = 8.0;
     33     }
     34     if (3 == myid) {
     35         for ( j=0; j<N; j++)
     36             myRows[SIZE][j] = 8.0;
     37     }
     38     for ( i = 1; i<SIZE+1; i++)
     39     {
     40         myRows[i][0] = 8.0;
     41         myRows[i][N-1] = 8.0;
     42     }
     43     /*Jacobi Iteration部分*/
     44     int step;
     45     for ( step = 0; step < T; step++ )
     46     {
     47         // 传递数据
     48         if (myid<3) {
     49             // 从 下方 进程接收数据
     50             MPI_Recv(&myRows[SIZE+1][0], N, MPI_FLOAT, myid+1, 0, MPI_COMM_WORLD, &status);
     51         }
     52         if (myid>0) {
     53             // 向 上方 进程发送数据
     54             MPI_Send(&myRows[1][0], N, MPI_FLOAT, myid-1, 0, MPI_COMM_WORLD);
     55         }
     56         if (myid<3) {
     57             // 向 下方 进程发送数据
     58             MPI_Send(&myRows[SIZE][0], N, MPI_FLOAT, myid+1, 0, MPI_COMM_WORLD);
     59         }
     60         if (myid>0) {
     61             // 从 上方 进程接收数据
     62             MPI_Recv(&myRows[0][0], N, MPI_FLOAT, myid-1, 0, MPI_COMM_WORLD, &status);
     63         }
     64         // 计算
     65         int r_begin, r_end;
     66         r_begin = (0==myid) ? 2 : 1;
     67         r_end = (3==myid) ? SIZE-1 : SIZE;
     68         for ( i = r_begin; i<=r_end; i++)
     69         {
     70             for ( j = 1; j<N-1; j++)
     71                 myRows2[i][j] = 0.25*(myRows[i][j-1]+myRows[i][j+1]+myRows[i-1][j]+myRows[i+1][j]);
     72         }
     73         // 更新
     74         for ( i = r_begin; i<=r_end; i++)
     75         {
     76             for (j = 1; j<N-1; j++)
     77                 myRows[i][j] = myRows2[i][j];
     78         }
     79     }
     80    // MPI_Barrier(MPI_COMM_WORLD);
     81     print_myRows(myid, myRows);
     82     MPI_Finalize();
     83 }
     84 
     85 void print_myRows(int myid, float myRows[][N])
     86 {
     87     int i,j;
     88     int buf[1];
     89     MPI_Status status;
     90     buf[0] = 1;
     91     if ( myid>0 ) {
     92         MPI_Recv(buf, 1, MPI_INT, myid-1, 0, MPI_COMM_WORLD, &status);
     93     }
     94     printf("Result in process %d:\n", myid);
     95     for ( i = 0; i<SIZE+2; i++)
     96     {
     97         for ( j = 0; j<N; j++)
     98             printf("%1.3f\t", myRows[i][j]);
     99         printf("\n");
    100     }
    101     if ( myid<3 ) {
    102         MPI_Send(buf, 1, MPI_INT, myid+1, 0, MPI_COMM_WORLD);
    103     }
    104     MPI_Barrier(MPI_COMM_WORLD);
    105 }
    复制代码

    代码执行结果:

    代码分析:

    (1)矩阵分块方式

      先说明一下,这与原书上的代码设计思路有差别:都老师的书上是把矩阵按columns划分的,我上面这份代码是将矩阵按照row分块的。

      书上为什么要按照列进行矩阵分块呢?我觉得原因是FORTAN的二维数组是按照列来优先存储元素的,所以按列划分矩阵,对于后面的MPI_Send MPI_Recv操作都比较方便。

      而我使用的是C语言来实现,C语言的二维数组是优先按照行来存储的,所以按照行来划分矩阵更划算,对于后面的MPI_Recv和MPI_Send操作更方便。

      ***关于矩阵分块的方式与矩阵乘法***

      有关这个部分,我查阅了一本《高性能并行计算》的讲义:http://www.sccas.cn/yhfw/wdypx/wd/lszl/201112/W020111215333260773702.pdf

      参阅了这本讲义的第3.1节内容,矩阵A×矩阵B并行计算的四种矩阵划分方式:行行、行列、列行、列列

      这几种方法的核心就是:固定A或B,移动一个;或都固定,移动中间结果。

      如果忘记了算法是如何移动数据的,通过画图的方式可以帮助理解:在每个时间片内,画出各个处理机有哪些数据,每个处理机内部进行了哪些运算。

      另外,这些算法的效率比较主要考察“数据交换量”和“计算量”两个指标。

    (2)代码设计逻辑

      这个算法并不复杂,比较重要的是如何在对等模式下,各个进程要互相发送和接受数据。如何设计通信次序才能保证进程之间没有死锁出现呢?

      关于这个问题,可以回顾一下都老师这本书上“7.7编写安全的MPI程序”。

      我的理解就是:如果进程A和进程B需要互相发送和接收数据,画出进程之间交换数据的调用次序图,如果次序图中没有两个箭头是交叉的,那么就认为不会出现通信死锁

      上面这个程序,通信调用次序图如下:

        

      可以看到,上述的通信调用次序图中,没有两个箭头是交叉的,所以是安全的。

      同时,也可以看到上面的程序关于进程通信的部分跟计算部分是完全分开的:

      通信部分只需要传递少量边界数据,计算部分充分利用各个处理器的并行计算优势。

     

    1.3 捆绑发送接受的Jacobi迭代实现

    代码实现:

    复制代码
      1 #include "mpi.h"
      2 #include <stdio.h>
      3 #include <stdlib.h>
      4 
      5 #define N 8 
      6 #define SIZE N/4
      7 #define T 2 
      8 
      9 
     10 void print_myRows(int, float [][N]);
     11 
     12 int main(int argc, char *argv[])
     13 {
     14     float myRows[SIZE+2][N], myRows2[SIZE+2][N];
     15     int myid;
     16     MPI_Status status;
     17 
     18     MPI_Init(&argc, &argv);
     19     MPI_Comm_rank(MPI_COMM_WORLD, &myid);
     20 
     21     int i,j;
     22     /*初始化*/
     23     for ( i = 0; i<SIZE+2; i++)
     24     {
     25         for ( j = 0; j<N; j++)
     26         {
     27             myRows[i][j] = myRows2[i][j] = 0;
     28         } 
     29     }
     30     if ( 0 == myid) {
     31         for ( j = 0; j<N; j++)
     32             myRows[1][j] = 8.0;
     33     }
     34     if (3 == myid) {
     35         for ( j=0; j<N; j++)
     36             myRows[SIZE][j] = 8.0;
     37     }
     38     for ( i = 1; i<SIZE+1; i++)
     39     {
     40         myRows[i][0] = 8.0;
     41         myRows[i][N-1] = 8.0;
     42     }
     43     /*Jacobi Iteration部分*/
     44     int step;
     45     for ( step = 0; step < T; step++ )
     46     {
     47         // 从上往下平移数据
     48         if ( 0 == myid) {
     49             MPI_Send(&myRows[SIZE][0], N, MPI_FLOAT, myid+1, 0, MPI_COMM_WORLD);
     50         }
     51         else if (3 == myid) {
     52             MPI_Recv(&myRows[0][0], N, MPI_FLOAT, myid-1, 0, MPI_COMM_WORLD, &status);
     53         }
     54         else {
     55             MPI_Sendrecv(&myRows[SIZE][0], N, MPI_FLOAT, myid+1, 0, &myRows[0][0], N, MPI_FLOAT, myid-1, 0, MPI_COMM_WORLD, &status);
     56         }
     57         // 从下向上平移数据
     58         if (3 == myid) {
     59             MPI_Send(&myRows[1][0], N, MPI_FLOAT, myid-1, 0, MPI_COMM_WORLD);
     60         }
     61         else if (0 == myid) {
     62             MPI_Recv(&myRows[SIZE+1][0], N, MPI_FLOAT, myid+1, 0, MPI_COMM_WORLD, &status);
     63         }
     64         else {
     65             MPI_Sendrecv(&myRows[1][0], N, MPI_FLOAT, myid-1, 0, &myRows[SIZE+1][0], N, MPI_FLOAT, myid+1, 0, MPI_COMM_WORLD, &status);
     66         }
     67         // 计算
     68         int r_begin, r_end;
     69         r_begin = (0==myid) ? 2 : 1;
     70         r_end = (3==myid) ? SIZE-1 : SIZE;
     71         for ( i = r_begin; i<=r_end; i++)
     72         {
     73             for ( j = 1; j<N-1; j++)
     74                 myRows2[i][j] = 0.25*(myRows[i][j-1]+myRows[i][j+1]+myRows[i-1][j]+myRows[i+1][j]);
     75         }
     76         // 更新
     77         for ( i = r_begin; i<=r_end; i++)
     78         {
     79             for (j = 1; j<N-1; j++)
     80                 myRows[i][j] = myRows2[i][j];
     81         }
     82     }
     83     MPI_Barrier(MPI_COMM_WORLD);
     84     print_myRows(myid, myRows);
     85     MPI_Finalize();
     86 }
     87 
     88 void print_myRows(int myid, float myRows[][N])
     89 {
     90     int i,j;
     91     int buf[1];
     92     MPI_Status status;
     93     buf[0] = 1;
     94     if ( myid>0 ) {
     95         MPI_Recv(buf, 1, MPI_INT, myid-1, 0, MPI_COMM_WORLD, &status);
     96     }
     97     printf("Result in process %d:\n", myid);
     98     for ( i = 0; i<SIZE+2; i++)
     99     {
    100         for ( j = 0; j<N; j++)
    101             printf("%1.3f\t", myRows[i][j]);
    102         printf("\n");
    103     }
    104     if ( myid<3 ) {
    105         MPI_Send(buf, 1, MPI_INT, myid+1, 0, MPI_COMM_WORLD);
    106     }
    107     MPI_Barrier(MPI_COMM_WORLD);
    108 }
    复制代码

    代码分析:

    如果一个进程既发送数据又接收数据,则可以使用MPI_Sendrecv函数来实现。

    比如Jacobi迭代中的中间两个进程,都需要发送和接收。这样处理起来就可以用MPI_Sendrecv函数。

    上述代码量并没有减少,但是设计逻辑稍微直观了一些。

     

    1.4 引入虚拟进程的Jacobi迭代

    代码实现:

    复制代码
     1 #include "mpi.h"
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 
     5 #define N 8 
     6 #define SIZE N/4
     7 #define T 2 
     8 
     9 
    10 void print_myRows(int, float [][N]);
    11 
    12 int main(int argc, char *argv[])
    13 {
    14     float myRows[SIZE+2][N], myRows2[SIZE+2][N];
    15     int myid;
    16     MPI_Status status;
    17 
    18     MPI_Init(&argc, &argv);
    19     MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    20 
    21     int i,j;
    22     /*初始化*/
    23     for ( i = 0; i<SIZE+2; i++)
    24     {
    25         for ( j = 0; j<N; j++)
    26         {
    27             myRows[i][j] = myRows2[i][j] = 0;
    28         } 
    29     }
    30     if ( 0 == myid) {
    31         for ( j = 0; j<N; j++)
    32             myRows[1][j] = 8.0;
    33     }
    34     if (3 == myid) {
    35         for ( j=0; j<N; j++)
    36             myRows[SIZE][j] = 8.0;
    37     }
    38     for ( i = 1; i<SIZE+1; i++)
    39     {
    40         myRows[i][0] = 8.0;
    41         myRows[i][N-1] = 8.0;
    42     }
    43     /*Jacobi Iteration部分*/
    44     int upid, downid;
    45     upid = (0==myid) ? MPI_PROC_NULL : myid-1;
    46     downid = (3==myid) ? MPI_PROC_NULL : myid+1;
    47     int step;
    48     for ( step = 0; step < T; step++ )
    49     {
    50         /*从上向下平移数据*/
    51         MPI_Sendrecv(&myRows[SIZE][0], N, MPI_FLOAT, downid, 0, &myRows[0][0], N, MPI_FLOAT, upid, 0, MPI_COMM_WORLD, &status);
    52         /*从下往上发送数据*/
    53         MPI_Sendrecv(&myRows[1][0], N, MPI_FLOAT, upid, 0, &myRows[SIZE+1][0], N, MPI_FLOAT, downid, 0, MPI_COMM_WORLD, &status);
    54         // 计算
    55         int r_begin, r_end;
    56         r_begin = (0==myid) ? 2 : 1;
    57         r_end = (3==myid) ? SIZE-1 : SIZE;
    58         for ( i = r_begin; i<=r_end; i++)
    59         {
    60             for ( j = 1; j<N-1; j++)
    61                 myRows2[i][j] = 0.25*(myRows[i][j-1]+myRows[i][j+1]+myRows[i-1][j]+myRows[i+1][j]);
    62         }
    63         // 更新
    64         for ( i = r_begin; i<=r_end; i++)
    65         {
    66             for (j = 1; j<N-1; j++)
    67                 myRows[i][j] = myRows2[i][j];
    68         }
    69     }
    70     MPI_Barrier(MPI_COMM_WORLD);
    71     print_myRows(myid, myRows);
    72     MPI_Finalize();
    73 }
    74 
    75 void print_myRows(int myid, float myRows[][N])
    76 {
    77     int i,j;
    78     int buf[1];
    79     MPI_Status status;
    80     buf[0] = 1;
    81     if ( myid>0 ) {
    82         MPI_Recv(buf, 1, MPI_INT, myid-1, 0, MPI_COMM_WORLD, &status);
    83     }
    84     printf("Result in process %d:\n", myid);
    85     for ( i = 0; i<SIZE+2; i++)
    86     {
    87         for ( j = 0; j<N; j++)
    88             printf("%1.3f\t", myRows[i][j]);
    89         printf("\n");
    90     }
    91     if ( myid<3 ) {
    92         MPI_Send(buf, 1, MPI_INT, myid+1, 0, MPI_COMM_WORLD);
    93     }
    94     MPI_Barrier(MPI_COMM_WORLD);
    95 }
    复制代码

    代码分析:

    Jacobi迭代的麻烦之处在于(按行划分矩阵):最上面的矩阵只需要与它下面的矩阵所在进程互相通信一次,最下面的矩阵只需要与它上面的矩阵所在进程互相通信一次,而中间的其余矩阵都需要与其上下相邻的矩阵所在进程通信两次。

    因此,必须对最上面和最下面的矩阵特殊处理,作为一种corner case来对待,所以代码麻烦。

    这里引入了MPI_PROC_NULL虚拟进程的概念,相当于给最上面的矩阵之上再来一个想象存在的进程:与这个虚拟进程通信不会真的通信,而是立刻返回。

    这个虚拟进程的意义在于可以方便处理corner case,如上面的例子,无论是代码量和设计思路都简化了许多,MPI替我们完成了很多工作。

     

    2. 主从模式MPI程序设计

    2.1 矩阵A×向量B=向量C

    主要通过矩阵A×向量B来讲解主从模式的程序设计,这个程序也是从FORTAN翻译成C代码的。

    大体思路如下:

    (1)一个master进程,负责总体调度,广播向量B到slaver进程,并把矩阵A的每一行发送到某个slaver进程

    (2)slaver一开始从master的广播中获得向量B,之后每次从master获得矩阵A的某一行(具体的行号,利用MPI_TAG发送;但是为了要把0行空出来作为结束标志,所以从1开始),计算矩阵A的该行与向量B的内积后再回传给master进程

    (3)master进程每次从一个slaver进程获得向量C的某个元素的值,master进程通过MPI_Recv中的status.MPI_TAG来判断该计算结果该更新到向量C的哪个位置中

    (4)如果master进程从某个slaver回收计算结果后,没有新的计算任务要派送给slaver进程了,就向slaver进程发送一个MPI_TAG=0的消息;slaver收到MPI_TAG=0的消息,就结束退出

    具体代码实现如下:

    复制代码
     1 #include "mpi.h"
     2 #include <stdio.h>
     3 
     4 #define ROWS 100
     5 #define COLS 100
     6 #define min(x,y) ((x)>(y)?(y):(x))
     7 int main(int argc, char *argv[])
     8 {
     9     int rows = 10, cols = 10;
    10     int master = 0;
    11     int myid, numprocs;
    12     int i,j;
    13     float a[ROWS][COLS], b[COLS], c[COLS];
    14     float row_result;
    15     MPI_Status status;
    16 
    17     MPI_Init(&argc, &argv);
    18     MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    19     MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
    20 
    21     /*master进程*/
    22     if (master == myid) {
    23         /*初始化矩阵a和b*/
    24         for (j=0; j<cols; j++) b[j]=1;
    25         for (i=0; i<rows; i++)
    26         {
    27             for (j=0; j<cols; j++)
    28             {
    29                 a[i][j] = i;
    30             }
    31         }
    32         /*只在master进程中初始化b 其余slave进程通过被广播的形式获得向量b*/
    33         MPI_Bcast(&b[0], cols, MPI_FLOAT, master, MPI_COMM_WORLD);
    34         /*向各个slave进程发送矩阵a的各行*/
    35         int numsent = 0;
    36         for ( i=1; i<min(numprocs,rows+1); i++)
    37         {
    38             /* 每个slave进程计算一行×一列的结果 这里用MPI_TAG参数标示对应结果向量c的下标+1
    39              * MPI_TAG在这里的开始取值范围是1 要把MPI_TAG空出来 作为结束slave进程的标志*/
    40             MPI_Send(&a[i-1][0], cols, MPI_FLOAT, i, ++numsent, MPI_COMM_WORLD);
    41         } 
    42         /*master进程接受其他各进程的计算结果*/
    43         for ( i=0; i<rows; i++)
    44         {
    45             /*类似poll的方法 只要有某个slave进程算出来结果了 MPI_Recv就能返回执行一次*/
    46             MPI_Recv(&row_result, 1, MPI_FLOAT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
    47             /*这里MPI_TAG的范围是1到rows 注意存储结果时下标减1*/
    48             c[status.MPI_TAG-1] = row_result;
    49             /*发送矩阵a中没发送完的行 就用刚返回计算结果空出来的那个slave进程 通过status.MPI_SOURCE找到这个空出来的进程*/
    50             if (numsent < rows) {
    51                 MPI_Send(&a[numsent][0], cols, MPI_FLOAT, status.MPI_SOURCE, numsent+1, MPI_COMM_WORLD);  
    52                 numsent = numsent + 1;
    53             }
    54             else { /*发送空消息 关闭slave进程*/
    55                 float close = 1.0;
    56                 MPI_Send(&close, 1, MPI_FLOAT, status.MPI_SOURCE, 0, MPI_COMM_WORLD);
    57             }
    58         }
    59         /*打印乘法结果*/
    60         for (j = 0; j < cols; j++ )
    61             printf("%1.3f\t", c[j]);
    62         printf("\n");
    63     }
    64     /*slave进程*/
    65     else {
    66         MPI_Bcast(&b[0], cols, MPI_FLOAT, master, MPI_COMM_WORLD);
    67         while(1)
    68         {
    69             row_result = 0;
    70             MPI_Recv(&c[0], cols, MPI_FLOAT, master, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
    71             if ( 0 != status.MPI_TAG ) {
    72                 for ( j = 0; j < cols; j++ )
    73                 {
    74                     row_result = row_result + b[j]*c[j]; 
    75                 }
    76                 //printf("myid:%d, MPI_TAG:%d, c[0]:%f, row_result:%1.3f\n", myid, status.MPI_TAG,c[0], row_result);
    77                 MPI_Send(&row_result, 1, MPI_FLOAT, master, status.MPI_TAG, MPI_COMM_WORLD);
    78             }
    79             else {
    80                 break;
    81             }
    82         }
    83     }
    84     MPI_Finalize();
    85 }
    复制代码

    代码执行结果:

    这里有两个细节需要注意:

    (1)关于++运算符在传递参数时的使用

      代码40行一开始我写成了“MPI_Send(&a[numsent][0], cols, MPI_FLOAT, i, ++numsent, MPI_COMM_WORLD);”

      显然,这个代码是错误的,问题的关键就出在了numsent这个变量上。

      比如,在执行这个语句前numsent的值为1。我希望第一个参数传递的是&a[1][0],第四个参数是2,并且numsent此时的值为2。

      这是一个思维陷阱,++numsent会导致numsent先加1,再作为值传递到MPI_Send实参中。所以,此时第一个参数变成了&a[2][0],并不是原先想要的。

      为了避免这种思维陷阱,以后再设计传递函数的参数时,应该禁止在传递参数时使用++这个运算符;多写一个赋值语句不会怎样,反之用++运算符就容易掉进思维陷阱。

    (2)关于#define带参宏定义的使用

      代码中用到#define min(x,y) ((x)>(y)?(y):(x))这个宏定义,这个地方稍微吃了一点儿坑。

      记住两个括号的原则:

      a. 每个变量都要加括号

      b. 宏定义整体要加括号

      如果想看看宏定义是否替换问正确的内容了,可以cc -E选项来查看预处理后的结果。

     

    2.2 master进程打印各个slaver进程发送来的消息

    代码实现:

    复制代码
     1 #include "mpi.h"
     2 #include <stdio.h>
     3 #include <string.h> 
     4 
     5 #define MSG_EXIT 1
     6 #define MSG_ORDERED 2
     7 #define MSG_UNORDERED 3
     8 
     9 
    10 void master_io();
    11 void slaver_io();
    12 
    13 int main(int argc, char *argv[])
    14 {
    15     int rank, size;
    16     MPI_Init(&argc, &argv);
    17     MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    18     if (0 == rank) {
    19         master_io();
    20     }
    21     else {
    22         slaver_io();
    23     }
    24     MPI_Finalize();
    25 }
    26 
    27 void master_io()
    28 {
    29     int i,j,size,nslaver,firstmsg;
    30     char buf[256], buf2[256];
    31     MPI_Status status;
    32     MPI_Comm_size(MPI_COMM_WORLD, &size);
    33     nslaver = size - 1;
    34     while (nslaver>0) {
    35         MPI_Recv(buf, 256, MPI_CHAR, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
    36         switch(status.MPI_TAG){
    37             case MSG_EXIT:
    38                 nslaver--;
    39                 break;
    40             case MSG_UNORDERED: // 如果是乱序就直接输出
    41                 fputs(buf, stdout);
    42                 break;
    43             case MSG_ORDERED:
    44                 firstmsg = status.MPI_SOURCE;
    45                 /* 这段程序设计的比较巧妙
    46                  * 虽然每个slaver发送两个message 不同slaver的message可能有重叠
    47                  * 但是每个每个slaver每次只能被接收一个message 
    48                  * 这一轮一旦接收到message了
    49                  * 就不再从这个slaver接收消息了
    50                  * 概括说:
    51                  * 第一次进入MSG_ORDERED处理各个slaver第一次调用MPI_Send发送的消息
    52                  * 第二次进入MSG_ORDERED处理各个slaver第二次调用MPI_Send发送的消息*/
    53                 for ( i=1; i<size; i++)                 
    54                 {
    55                     if (i==firstmsg) {
    56                         fputs(buf, stdout);
    57                     }
    58                     else {
    59                         MPI_Recv(buf2, 256, MPI_CHAR, i, MSG_ORDERED, MPI_COMM_WORLD, &status);
    60                         fputs(buf2, stdout);
    61                     }
    62                 }
    63                 break;
    64         }
    65     }
    66 }
    67 
    68 void slaver_io()
    69 {
    70     char buf[256];
    71     int rank;
    72     MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    73     /*第一次向master进程发送有序消息*/
    74     sprintf(buf, "Hello from slaver %d, ordered print\n", rank);
    75     MPI_Send(buf, strlen(buf)+1, MPI_CHAR, 0, MSG_ORDERED, MPI_COMM_WORLD);
    76     /*第二次向master进程发送有序消息*/
    77     sprintf(buf, "Bye from slaver %d, ordered print\n", rank);
    78     MPI_Send(buf, strlen(buf)+1, MPI_CHAR, 0, MSG_ORDERED, MPI_COMM_WORLD);
    79     /*第一次向master发送无序消息*/
    80     sprintf(buf, "I'm exiting (%d), unordered print\n", rank);
    81     MPI_Send(buf, strlen(buf)+1, MPI_CHAR, 0, MSG_UNORDERED, MPI_COMM_WORLD);
    82     /*发送结束信息*/
    83     MPI_Send(buf, 0, MPI_CHAR, 0, MSG_EXIT, MPI_COMM_WORLD);
    84 }
    复制代码

    有一个不错的小巧的设计思路,在代码注释中具体说明了,这里不再赘述。

    把这个例子看明白了,非常有利于理解master slaver的设计模式,并且知道如何花式保证每次master都能接受发送自slaver相同“批次”的消息。

     

    小结:

    看完了这章内容,对MPI程序设计稍微有些感觉了。另外,感觉前面撸了一遍APUE的帮助挺大的,虽然MPI程序接口比较简单,但是总让我想到APUE中的fork+IPC的部分。

    展开全文
  • MPI程序 对等模式的 Jacobi迭代

    千次阅读 2015-07-27 10:11:05
    对等模式的MPI程序:test_8_1_2.c Jacobi迭代:迭代数据按列进行分割, 并假设一共有4个进程同时并行计算。时间:15.7.27 Jason Zhou 热爱你所写下的程序,他是你的伙伴,而不是工具. */ #include "mpi.h" #...
    /*
       对等模式的MPI程序:test_8_1_2.c
       Jacobi迭代:迭代数据按列进行分割,
       并假设一共有4个进程同时并行计算。
    
    
    
    时间:15.7.27
    Jason Zhou   
    热爱你所写下的程序,他是你的伙伴,而不是工具.
    */
    #include "mpi.h"
    #include <stdio.h>
    
    #define totalsize 8
    #define mysize totalsize/4    //分成四块,每块大小
    #define steps 10
    
    int  main(int argc,char* argv[])
    {
            int myid,numprocs,n,i,j,rc;
            float a[totalsize][mysize+2],b[totalsize][mysize+2],tmp[mysize][totalsize],c[totalsize][totalsize];  //除分块大小外,还包括左右两边各一列
            float temp[totalsize];  /* 临时数组 */
            int begin_col,end_col,ierr;
            MPI_Status status;
    
    
            //初始化c数组
            for(i=0;i<totalsize;i++)
            {
                    for(j=0;j<=totalsize;j++)
                            c[i][j]=0;
            }
    
            for(int j=0;j<mysize;j++)
                    for(int i=0;i<totalsize;i++)
                            tmp[j][i]=8;
    
    
            MPI_Init(&argc,&argv);
    
            /* 得到当前进程标识和总的进程个数 */
            MPI_Comm_rank(MPI_COMM_WORLD,&myid);
            MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
            fprintf(stderr,"Process %d of %d is alive.\n",myid,numprocs);
    
            /* 数组初始化 */
            //NOTE: 整个数组置值为0, 行数为 totalsize, 列数为 mysize+2
            for(j=0;j<mysize+2;j++)
                    for(i=0;i<totalsize;i++)
                            a[i][j]=0.0;
    
            if(myid==0)
                    for(i=0;i<totalsize;i++)
                            a[i][1]=8.0;
    
            if(myid==3)
                    for(i=0;i<totalsize;i++)
                            a[i][mysize]=8.0;
    
    
            for(i=1;i<mysize+1;i++){   
                    a[0][i]=8.0;
                    a[totalsize-1][i]=8.0;
            }    
            /* Jacobi迭代部分 */
            for(n=1;n<=steps;n++)
            {
                    //这里从两边分别"获取数据"和"发送数据",每次获取或发送数据时先判断是否会越界
                    /* 从右侧的邻居得到数据 */
                    if(myid<3)
                    {
                            MPI_Recv(&temp[0],totalsize,MPI_FLOAT,myid+1,10,MPI_COMM_WORLD,&status);
                            for(i=0;i<totalsize;i++)
                                    a[i][mysize+1]=temp[i];
                            //a[i][mysize+1]为块最右一列,表示相邻右侧一块最左边一列
                    }
                    /* 向左侧的邻居发送数据 */
                    if(myid>0)
                    {
                            for(i=0;i<totalsize;i++)
                                    temp[i]=a[i][1];
                            MPI_Send(&temp[0],totalsize,MPI_FLOAT,myid-1,10,MPI_COMM_WORLD);
                    }
                    /* 向右侧的邻居发送数据 */
                    if(myid<3)
                    {
                            for(i=0;i<totalsize;i++)
                                    temp[i]=a[i][mysize];
                            MPI_Send(&temp[0],totalsize,MPI_FLOAT,myid+1,10,MPI_COMM_WORLD);
                    }
                    /* 从左侧的邻居得到数据 */
                    if(myid>3)
                    {
                            MPI_Recv(&temp[0],totalsize,MPI_FLOAT,myid-1,10,MPI_COMM_WORLD,&status);
                            for(i=0;i<totalsize;i++)
                                    a[i][0]=temp[i];
                            //a[i][0] 为块最左一列,表示相邻左侧一块最右一列
                    }
    
                    begin_col=1;
                    end_col=mysize;
    
                    if(myid==0)
                            begin_col=2;
                    if(myid==3)
                            end_col=mysize-1;
    
    
                    for(j=begin_col;j<=end_col;j++)
                            for(i=1;i<totalsize-1;i++)
                                    b[i][j]=0.25*(a[i][j+1]+a[i][j-1]+a[i+1][j]+a[i-1][j]);
    
                    for(i=1;i<totalsize-1;i++)
                            for(j=begin_col;j<=end_col;j++)
                            {
                                    a[i][j]=b[i][j];
                            }
            }/*     迭代结束 */
            int loc=begin_col;
            if(0==myid)
                    loc--;
            for(i=1;i<totalsize-1;i++)
                    for(j=begin_col;j<=end_col;j++)
                    {
                            tmp[j-loc][i]=a[i][j];
                    }
    
    
            MPI_Barrier(MPI_COMM_WORLD);
    
    
            MPI_Gather(tmp,16,MPI_FLOAT,c,16,MPI_FLOAT,0,MPI_COMM_WORLD);//需要单独在外面
    
            /* 输出结果 */
            if(0==myid)
            {
                    fprintf(stderr,"\nProcess %d :\n",myid);
                    for(int j=0;j<mysize;j++)
                    {
                            for(int i=0;i<totalsize;i++)
                                    fprintf(stderr,"%.2f\t",tmp[j][i]);
                            fprintf(stderr,"\n");
                    }
                    fprintf(stderr,"\n");
    
                    fprintf(stderr,"\n收集后结果\n");
                    for(int i=0;i<totalsize;i++)
                    {
                            for(int j=0;j<totalsize;j++)
                                    fprintf(stderr,"%.2f\t",c[i][j]);
                            fprintf(stderr,"\n");
                    }
                    fprintf(stderr,"\n");
            }
            MPI_Finalize();
    }

    输出结果如下图
    输出结果

    展开全文
  • 对等模式与C/S模式的区别

    千次阅读 2010-07-11 14:54:00
    用户不仅可存取在服务器和本地工作站上的资源,还可以享用其他工作站上的资源,实现了资源共享。 <br />(2)可实现管理科学化和专业化。系统中的资源分布在各服务器和工作站上,可以采用分层管理和专业化管理...

    在客户机/服务器网络中,服务器是网络的核心,而客户机是网络的基础,客户机依靠服务器获得所需要的网络资源,而服务器为客户机提供网络必须的资源。
    优点:
    (1)可实现资源共享。C/S结构中的资源是分布的,客户机与服务器具有一对多的关系和运行环境。用户不仅可存取在服务器和本地工作站上的资源,还可以享用其他工作站上的资源,实现了资源共享。

    (2)可实现管理科学化和专业化。系统中的资源分布在各服务器和工作站上,可以采用分层管理和专业化管理相结合的方式,用户有权去充分利用本部门、本领域的专业知识来参与管理,使得各级管理更加科学化和专业化。

    (3)可快速进行信息处理。由于在 C/S 结构中是一种基于点对点的运行环境,当一项任务提出请求处理时,可以在所有可能的服务器间均衡地分布该项任务的负载。这样,在客户端发出的请求可由多个服务器来并行进行处理,为每一项请求提供了极快的响应速度和较高的事务吞吐量。

    (4)能更好地保护原有的资源。由于C/S是一种开放式的结构,可有效地保护原有的软、硬件资源。以前,在其他环境下积累的的数据和软件均可在C/S中通过集成而保留使用,并且可以透明地访问多个异构的数据源和自由地选用不同厂家的数据应用开发工具,具有高度的灵活性;而以前的硬件亦可完全继续使用,当在系统中增加硬件资源时,不会减弱系统的能力,同时客户机和服务器均可单独地升级,故具有极好的可扩充性。


    对等连接(peer-to-peer,简写为 P2P)是指两个主机在通行时并不区分哪一个是服务请求方还是服务提供方。只要两个主机都运行了对等连接软件(P2P软件),它们就可以进行平等的、对等连接通信。
    对等网是一种投资少、见效快、高性价比的实用型小型网络系统。

    对比:
    虽然对等网是平等的,但仍然不能共享可执行程序,只有上升到客户/服务器结构的局域网,才能共享服务器上的可执行程序。
    客户服务器模式,需要专人维护、管理服务器,增加费用。它比较适合较大的网络。像较小的网络。如10PC以下的,使用对等网就可以了

    展开全文
  • H3C S3600 交换机NTP对等模式的配置

    千次阅读 2010-09-20 12:05:00
    2.Switch A以Switch B作为时间服务器,将自己设置为客户端,则Switch B自动工作在服务器模式 ;3.Switch C将Switch A设为对等体。二、组网图三、配置 步骤:1  S3500系列交换机 NTP标准时钟配置Switch B的...
  • [common] DCPSGlobalTransportConfig=$file ORBDebugLevel=0 DCPSDebugLevel=10 DCPSTransportDebugLevel=0 ORBLogFile=sub.log [domain/2] DiscoveryConfig=fast_rtps [rtps_discovery/fast_rtps] ...
  • 基于InfiniBand网络的的对等模式还没有配置成功过,下面以以太网的配置方法为例说明其过程。 首先安装MIC驱动-MPSS包,详细安装过程见“MIC编程(5 )——MIC驱动MPSS安装” 通过service mpss status命令查看...
  • 对等网络

    2020-12-07 20:45:58
    在以太网中有多种组网方式,对等网络是最常见也是最基本的一种组网模式对等网是指规模比较小,一般由几十台以内的计算机构成的局域网。根据构成的数量的不同可以分为由两台,三台或三台以上的计算机构成的对等网。...
  • wifi的几种工作模式

    千次阅读 2018-12-11 23:49:20
    WIFI配置具体的模式主要有以下这几种:STA模式、AccessPoint模式、Monitor模式、Ad-hoc(IBSS)模式、WDS模式、Mesh模式。 第一种:STA模式  任何一种无线网卡都可以...这种模式下,wifi工作于从模式 第二种:Ac...
  • 无线网卡的四种工作模式

    千次阅读 2020-02-21 22:37:31
    无线网卡一般有四种工作模式,即 Managed,Master,Ad-hoc,Monitor。 Managed (被管理,即你的无线网卡处理被管理的模式模式:该模式用于你的无线客户端直接与无线接入点(Access Point,即AP)进行接入连接。...
  • 探讨对等网络(P2P)商务模式

    千次阅读 2001-02-05 15:32:00
    我们可以把Popular Power模式称为“水利电站”模式——汇流百川,蓄势而发,也可以将其称为“商贩模式”——通过贩卖个人计算机的闲置资源来获取利润。 · DataSynapse模式  DataSynapse公司赢利的方法是...
  • 在WCF中有三种消息交换模式(Message Exchange Pattern,MEP),包括单向通信模式、双工通信模式、请求-响应通信模式,其通过5种不同的信道来实现,其中不是每一种绑定都能支持这些信道,具体可以通过程序进行检测,...
  • linux 无线网卡 工作模式 wifi模式

    千次阅读 2019-01-15 16:13:16
    无线网卡一般有四种工作模式,即 Managed,Master,Ad-hoc,Monitor。 Managed 模式:该模式用于你的无线客户端直接与无线接入点(Access Point,即AP)进行接入连接。在这个模式中,我们可以进行无线接入internet...
  • 《智慧的物联网——...本节为大家介绍互联网应用的两种工作模式:客户/服务器模式与P2P模式。 AD:WOT2014课程推荐:实战MSA:用开源软件搭建微服务系统 3.3.3 互联网应用的两种工作模式:客户/服
  • 无线网卡工作模式

    千次阅读 2014-02-27 15:44:29
    无线网卡一般有四种工作模式,即Managed,Master,Ad-hoc,Monitor。 Managed 模式:该模式用于你的无线客户端直接与无线接入点(Access Point,即AP)进行接入连接。在这个模式中,我们可以进行无线接入internet...
  • 无线网卡的工作模式

    千次阅读 2011-10-24 11:37:24
    无线网卡可以工作在多种模式之下。常见的有Master,Managed,ad-hoc,monitor等模式。   对于Master模式,它主要使用于无线接入点AP提供无线接入服务以及路由功能。可以想象我们使用的无线路由器就是工作在Maste
  • cpu的工作模式

    千次阅读 2009-10-09 23:46:00
    模式的问题与保护模式的出现 三。386以上处理器的特点 四。保护模式下的地址转换 一。x86实模式介绍 x86体系的处理器刚开始时只有20根地址线,寻址寄存器是16位。我们知道16位的寄存器可以访问64K的地址空间,如果...
  •  对等模式的MPI程序:test_8_1_2.c    Jacobi迭代:迭代数据按列进行分割,    并假设一共有4个进程同时并行计算。   */   #include "mpi.h"   #include <stdio.h>     #define totalsize 16 ...
  • Linux无线网卡的工作模式

    千次阅读 2019-01-18 21:07:34
    无线网卡一般有四种工作模式,即 Managed,Master,Ad-hoc,Monitor。 Managed 模式:该模式用于你的无线客户端直接与无线接入点(Access Point,即AP)进行接入连接。在这个模式中,我们可以进行无线接入...
  • wifi网卡工作模式和iwconfig

    千次阅读 2018-12-05 19:50:08
    原帖地址:http://www.openwrt.org.cn/bbs/forum.php?mod=viewthread&amp;tid=474   ... 无线网卡常见的工作模式有Master、Managed、ad-hoc、monitor等。   对于Master模式,它主要使用于无线接入...
  • 无线网卡的4种工作模式

    千次阅读 2013-11-04 10:10:25
    无线网卡的4种工作模式 本文转自:http://book.51cto.com/art/201303/385874.htm   在开始嗅探无线数据包之前,我们需要了解无线网卡的不同工作模式。 无线网卡一共有4种工作模式。   被管理模式(Managed ...
  • 组建对等局域网

    2018-05-01 09:05:17
    1、对等网络:... 在P2P网络环境中,彼此连接的多台计算机之间都处于对等的地位,各台计算机有相同的功能,无主从之分,一台计算机既可作为服务器,设定共享资源供网络中其他计算机所使...
  • 打开VMware→虚拟机→设置。如下图   一、 桥接模式(Bridge...此时虚拟机与网络中其他机器的地位是对等的虚拟机能否连接外网取决于路由器的相关设置。具体配置如下: 二、 NAT模式   1工作原理   ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 45,277
精华内容 18,110
关键字:

对等工作模式