【问题标题】:Implementing MPI_Reduce with MPI_Send and MPI_Recv leads to wrong results使用 MPI_Send 和 MPI_Recv 实现 MPI_Reduce 会导致错误的结果
【发布时间】:2021-05-09 01:35:41
【问题描述】:

我正在开发一个使用MPI_Send()MPI_Recv() 替换MPI_Reduce() 的程序。

除了给出 PI 近似值、错误和运行时间的代码的最后几位之外,我可以运行所有内容。收到后我也没有得到正确的总和值。

我相信MPI_Recv() 端出了点问题,但我可能是错的。我在运行它时只使用了 2 个处理器。使用MPI_Reduce 时,无需将 PI 初始化为某个值,该程序就可以正常工作。

#include "mpi.h"
#include <stdio.h>
#include <math.h>
 
int main( int argc, char *argv[])
{
    int n, i;
    double PI25DT = 3.141592653589793238462643;
    double pi, h, sum, x;
 
    int size, rank;
    double startTime, endTime;
 
    /* Initialize MPI and get number of processes and my number or rank*/
    MPI_Init(&argc,&argv);
    MPI_Comm_size(MPI_COMM_WORLD,&size);
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);
 
    /* Processor zero sets the number of intervals and starts its clock*/
    if (rank==0)
    {
       n=600000000;
       startTime=MPI_Wtime();
       for (int i = 0; i < size; i++) {
           if (i != rank) {
               MPI_Send(&n, 1, MPI_INT, i, 0, MPI_COMM_WORLD);
           }
       }
    } 
    /* Broadcast number of intervals to all processes */
    else 
    {
        MPI_Recv(&n, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
    }
 
    /* Calculate the width of intervals */
    h   = 1.0 / (double) n;
 
    /* Initialize sum */
    sum = 0.0;
    /* Step over each inteval I own */
    for (i = rank+1; i <= n; i += size)
    {
        /* Calculate midpoint of interval */
        x = h * ((double)i - 0.5);
        /* Add rectangle's area = height*width = f(x)*h */
        sum += (4.0/(1.0+x*x))*h;
    }
    /* Get sum total on processor zero */
    //MPI_Reduce(&sum,&pi,1,MPI_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD);
    MPI_Send(&sum, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
    MPI_Send(&pi, 1, MPI_SUM, 0, 0, MPI_COMM_WORLD);
    
    if (rank == 0) 
    {
        double total_sum = 0;
        for (int i = 0; i < size; i++) 
        {
            MPI_Recv(&sum, 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
            MPI_Recv(&pi, 1, MPI_SUM, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
            total_sum += sum;
            printf("Total Sum is %lf\n", total_sum);
        }
    }
    
    /* Print approximate value of pi and runtime*/
    if (rank==0)
    {
       printf("pi is approximately %.16f, Error is %e\n",
                       pi, fabs(pi - PI25DT));
       endTime=MPI_Wtime();
       printf("runtime is=%.16f",endTime-startTime);
    }
    MPI_Finalize();
    return 0;
}

【问题讨论】:

    标签: c performance parallel-processing mpi hpc


    【解决方案1】:

    这个

    MPI_Send(&pi, 1, MPI_SUM, 0, 0, MPI_COMM_WORLD);
                     ^^^^^^^
    

     MPI_Recv(&pi, 1, MPI_SUM, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
                      ^^^^^^^
    

    错误的是 MPI_SendMPI_Recv 期望作为第三个参数 MPI_Datatype 而不是 MPI_OPMPI_SUM)。

    但是查看您的代码,您真正想要做的是将这些调用替换为:

    double pi = sum;
    if (myid != 0) {
            MPI_Send(&sum, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
    }
    else {
        for (int i = 1; i < numprocs; i++) {
            MPI_Recv(&value, 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
            pi += value;
            }
    }
    

    替换MPI_Reduce的行为。

    一个运行的例子:

    #include "mpi.h"
    #include <stdio.h>
    #include <math.h>
     
    int main( int argc, char *argv[])
    {
        int n, i;
        double PI25DT = 3.141592653589793238462643;
        double h, sum, x;
     
        int numprocs, myid;
        double startTime, endTime;
     
        /* Initialize MPI and get number of processes and my number or rank*/
        MPI_Init(&argc,&argv);
        MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
        MPI_Comm_rank(MPI_COMM_WORLD,&myid);
     
        /* Processor zero sets the number of intervals and starts its clock*/
        if (myid==0) {
           n=600000000;
           startTime=MPI_Wtime();
           for (int i = 0; i < numprocs; i++) {
               if (i != myid) {
                   MPI_Send(&n, 1, MPI_INT, i, 0, MPI_COMM_WORLD);
               }
           }
        } 
        else {
            MPI_Recv(&n, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        }
     
        /* Calculate the width of intervals */
        h   = 1.0 / (double) n;
     
        /* Initialize sum */
        sum = 0.0;
        /* Step over each inteval I own */
        for (i = myid+1; i <= n; i += numprocs) {
            /* Calculate midpoint of interval */
            x = h * ((double)i - 0.5);
            /* Add rectangle's area = height*width = f(x)*h */
            sum += (4.0/(1.0+x*x))*h;
        }
        /* Get sum total on processor zero */
        //MPI_Reduce(&sum,&pi,1,MPI_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD);
        double value = 0;
        double pi = sum;
        if (myid != 0) {
                MPI_Send(&sum, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
        }
        else {
            for (int i = 1; i < numprocs; i++) {
                MPI_Recv(&value, 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
                pi += value;
                }
        }
        
        /* Print approximate value of pi and runtime*/
        if (myid==0) {
           printf("pi is approximately %.16f, Error is %e\n",
                           pi, fabs(pi - PI25DT));
           endTime=MPI_Wtime();
           printf("runtime is=%.16f",endTime-startTime);
        }
        MPI_Finalize();
        return 0;
    }
    

    输出(2个进程):

    pi is approximately 3.1415926535898993, Error is 1.061373e-13
    runtime is=1.3594319820404053
    

    【讨论】:

      猜你喜欢
      • 2021-05-07
      • 2019-03-18
      • 1970-01-01
      • 2018-03-05
      • 2011-11-11
      • 2019-06-08
      • 2015-04-22
      • 1970-01-01
      • 2014-04-17
      相关资源
      最近更新 更多