MPI如何使根进程中初始化的变量在其他进程中可见

时间:2016-02-14 23:10:18

标签: c++ parallel-processing mpi

我有几个初始化数组a0的测试方法,但是每个方法的数组大小不同。这些数组在进程rank == 0中初始化。现在在其他文件中,我有一个方法的实现,该方法使用该表中的值进行一些计算(它不会修改它们)。 它看起来有点像(伪代码):

res=0;
x=0;
for(i=mystart;i<myend;++i)
  for(j=0;j<length;++j)
    x+= a[j] * multiplier;

MPI_Reduce(&x,&res,1,MPI_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD);

我要求这些计算只能由root以外的进程处理,但由于他们不知道[]持有什么值,所以它不起作用。我尝试了MPI_Bcast(a,?,MPI_DOUBLE,0,MPI_COMM_WORLD),但我无法获得大小的值,因为它与每个方法调用不同。整个代码有点大,但如果有人想看看我可以在某个地方发布它......

是否有一种简单的方法可以让这些字段对其他进程可见?

编辑: 正如建议我上传代码: 主要功能:http://pastebin.com/tREeHMZM 计算和类声明的方法:http://pastebin.com/BBedJ7bA

将循环划分得很好,只是那些不起作用的计算,而且我不确定如何让这些过程相互通信。

2 个答案:

答案 0 :(得分:0)

为什么不广播您首先要播放的阵列的大小?

答案 1 :(得分:0)

你真的很亲密。我相信我能够纠正该程序,更正的文本在下面。

注意:在我实际修改程序之前,我开始写这个解释,所以替换&#34;你必须做&#34;用&#34;我做了&#34;

我能够下载并构建您的程序。通过将Function.h更改为Function::value [全局函数],我能够根据已有的Function_value来解决这个问题。

要做的一件事就是让等级0得到随机种子值。它必须首先播放 ,因此客户端排名可以获得它,因此他们对shuffle的调用将产生相同的结果。因此,我创建了一个全局ranseed,来自main中的bcast,shuffle可以看到它。

我将test*例程更改为始终计算x0y0ac,无论等级如何。否则,编译器抱怨这些指针可能未初始化的值。计算量很小。无需广播这些值。如果这种预先计算是密集的[它不是这样的话],那么对阵列进行预测将是最佳选择。

我认为你已经在calcError的MPI版本中意识到了这一点,因为你正在评论这些数组的bcast。

AFAICT,你的MPI calcError看起来不错除了两件事:

(1)MPI_Reduce调用应该if (myid_c >= 0)块内。我认为它需要在此块之后位于底部(即使等级0需要调用它)。

(2)在MPI_Reduce [{1}}]内的位置,我认为在双级for循环之后需要if因为error_p = error;是客户发送的值 [和error_p是根收到的]

我添加了error命令行选项,允许手动设置随机种子值。我还添加了一个-R选项,用于在-M的MPI和非MPI版本之间切换

我使用mpi模式运行程序:calcError并且所有测试似乎都有效。一段时间后我在mpirun -np 8 <program> -Mctrl-c做了testL,因此无法保证。

这是一个[存档]文件,类似于您的粘贴,文件分隔线为% <filename>

它有我所有的修正[请原谅无偿的风格清理]。

警告:提取Function.h时要小心,因为我必须创建一个骨架版本,所以如果您的版本有类定义,它就会丢失。

% Error.h
// mpisize/Error.h -- Error class

#ifndef _mpisize_Error_h_
#define _mpisize_Error_h_

class Error {
  private:
    double *v;
    double *x0;
    double *y0;
    double *a;
    double *c;
    int length;                         // grid length
    double length_2;
    double mult;
    int size;                           // number of gausses to add
    double error;

  public:
    void setValues(double *v, int length, double mult);
    void setCoefficients(double *x0,double *y0, double *a,double *c, int size);
    void calcError(int mpiflg);
    void calcErrorStd();
    void calcErrorMpi();
    double getError();
};

#endif
% Function.h
// mpisize/Function.h -- Function class

#ifndef _mpisize_Function_h_
#define _mpisize_Function_h_

#include <math.h>

//Function code
double
Function_value(double x, double y, double x0, double y0, double a, double c);

#endif
% mpisize.h
// mpisize/mpisize.h -- Function class

#ifndef _mpisize_mpisize_h_
#define _mpisize_mpisize_h_

#include <math.h>
#include "Error.h"
#include "Function.h"

#ifdef _MPISIZE_GLO_
#define EXTRN_MPISIZE       /**/
#else
#define EXTRN_MPISIZE       extern
#endif

EXTRN_MPISIZE int opt_debug;
EXTRN_MPISIZE int opt_send;
EXTRN_MPISIZE int glob_rank;
EXTRN_MPISIZE double tvzero;

double
tvgetf(void);

int
dbgif(int lvl);

void
_dbgprt(const char *fmt,...);

#define dbgprt(_lvl,_fmt...) \
    do { \
        if (dbgif(_lvl)) \
            _dbgprt(_fmt); \
    } while (0)

#endif
% ErrMpi.cpp
// mpisize/ErrMpi -- MPI threaded version of calcError

#include <iostream>
#include <math.h>
#include <mpi.h>
#include "mpisize.h"

void
Error::calcErrorMpi()
{
    int mystart, myend;
    int myid, myid_c;
    int numproc, numproc_c;

    //MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    myid = glob_rank;
    MPI_Comm_size(MPI_COMM_WORLD, &numproc);

    myid_c = myid - 1;
    numproc_c = numproc - 1;

    // NOTE/BUG: length_2 is double, so it needs MPI_DOUBLE
#if 0
    MPI_Bcast(&length, 1, MPI_INT, 0, MPI_COMM_WORLD);
    // MPI_Bcast(&length_2, 1, MPI_INT, 0, MPI_COMM_WORLD);  // broken
    // MPI_Bcast(&length_2, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);  // fixed
#endif

    MPI_Bcast(&error, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
    // MPI_Bcast(v, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);

    MPI_Bcast(&size, 1, MPI_INT, 0, MPI_COMM_WORLD);
    MPI_Bcast(&mult, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);

    // NOTE/BUG: these are arrays, so second argument must be array count
    if (opt_send) {
        MPI_Bcast(x0, size, MPI_DOUBLE, 0, MPI_COMM_WORLD);
        MPI_Bcast(y0, size, MPI_DOUBLE, 0, MPI_COMM_WORLD);
        MPI_Bcast(a, size, MPI_DOUBLE, 0, MPI_COMM_WORLD);
        MPI_Bcast(c, size, MPI_DOUBLE, 0, MPI_COMM_WORLD);
    }

    double error_p = 0;

    error = 0;

    if (myid_c >= 0) {
        mystart = (length / numproc_c) * myid_c;

        if (length % numproc_c > myid_c) {
            mystart += myid_c;
            myend = mystart + (length / numproc_c) + 1;
        }
        else {
            mystart += length % numproc_c;
            myend = mystart + (length / numproc_c);
        }

        dbgprt(2,"calcErrorMpi: STARTUP myid_c=%d numproc_c=%d length=%d length_2=%g mystart=%d myend=%d\n",
            myid_c,numproc_c,length,length_2,mystart,myend);

        double dv;
        double vtmp;

        for (int i = mystart; i < myend; i++) {
            for (int j = 0; j < length; j++) {
                vtmp = 0.0;
                for (int k = 0; k < size; k++)
                    vtmp += Function_value((i - length_2) * mult,
                        (j - length_2) * mult,
                        x0[k], y0[k], a[k], c[k]);
                dv = v[i * length + j] - vtmp;
                error += dv * dv;
            }
        }

        error_p = error;

        // if(myid == 0 ) cout << "Proces " << myid << " after reducing error = " << error << endl;

    }

    dbgprt(2,"calcErrorMpi: RED/BEF error_p=%g error=%g\n",error_p,error);
    MPI_Reduce(&error_p, &error, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
    dbgprt(2,"calcErrorMpi: RED/AFT error_p=%g error=%g\n",error_p,error);
}
% Error.cpp
// mpisize/Error -- Error routines

#include <iostream>
#include <math.h>
#include "Error.h"
#include "Function.h"

using namespace std;

void
Error::setValues(double *v, int length, double mult)
{
    this->v = v;
    this->length = length;
    this->mult = mult;
    length_2 = length * 0.5;
    cout << "test" << endl;
}

void
Error::setCoefficients(double *x0, double *y0, double *a, double *c, int size)
{
    this->x0 = x0;
    this->y0 = y0;
    this->a = a;
    this->c = c;
    this->size = size;
}

void
Error::calcErrorStd()
{
    double dv;
    double vtmp;

    error = 0;

    for (int i = 0; i < length; i++) {
        for (int j = 0; j < length; j++) {
            vtmp = 0.0;
            for (int k = 0; k < size; k++)
                vtmp += Function_value((i - length_2) * mult,
                    (j - length_2) * mult,
                    x0[k], y0[k], a[k], c[k]);
            dv = v[i * length + j] - vtmp;
            error += dv * dv;
        }
    }
}

void
Error::calcError(int mpiflg)
{

    if (mpiflg)
        calcErrorMpi();
    else
        calcErrorStd();
}

double
Error::getError()
{
    return sqrt(error);
}
% Function.cpp
// mpisize/Function -- function code

#include "Function.h"

// Function code
double
Function_value(double x, double y, double x0, double y0, double a, double c )
{
   return a * exp(-((x - x0) * (x - x0) + (y - y0) * (y - y0)) * c);
}
% mpisize.cpp
// mpisize/mpisize -- main program and test routines

#define _MPISIZE_GLO_
#include "mpisize.h"
#include "Error.h"
#include "Function.h"

#include <iostream>
#include <mpi.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/time.h>
#include <math.h>

using namespace std;

const int LEN = 1250;
const int LEN_2 = LEN / 2;
const int SIZE = LEN * LEN;
const int SHUFFLE_TIMES = 2 * SIZE;
const double MULT = 0.1;
const int COMPLEXITY = 8;

int ranseed;
int opt_mpi;

double
generate(double x, double y, double *x0, double *y0, double *a, double *c, int indexS, int indexE)
{
    double v = 0.0;

    for (int i = indexS; i < indexE; i++) {
        v += Function_value(x, y, x0[i], y0[i], a[i], c[i]);
    }
    return v;
}

void
generate(double *v, double *x0, double *y0, double *a, double *c)
{
    for (int i = 0; i < LEN; i++)
        for (int j = 0; j < LEN; j++)
            v[i * LEN + j] = generate((i - LEN_2) * MULT, (j - LEN_2) * MULT,
                x0, y0, a, c, 0, COMPLEXITY);
}

void
shuffle(double *v)
{
    // losowo przestawiamy polozenia czastek
    int i;
    int j;
    double vtmp;

    srandom(ranseed);

    for (int k = 0; k < SHUFFLE_TIMES; k++) {
        i = random() % SIZE;
        j = random() % SIZE;

        vtmp = v[i];

        v[i] = v[j];
        v[i] = vtmp;
    }
}

void
test(const char *testname,Error *err, double errorExpected)
{

    err->calcError(opt_mpi);

    double error;

    if (glob_rank == 0) {
        error = err->getError();
        cout << endl << "Test " << testname << " blad " << error << endl;

        if (fabs(error - errorExpected) > (0.001 + 0.001 * errorExpected)) {
            cerr << "Blad powinno byc " << errorExpected << endl;
            MPI_Finalize();
            exit(0);
        }
        else {
            cout << " - - - - - - OK" << endl;
        }
    }
}

void
test1(Error *err)
{

    double x0[] = { -3, -2, -2, -1, 1, 2, 2, 3 };
    double y0[] = { -3, -3, 3, -1, 1, -3, 3, 3 };
    double a[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
    double c[] = { 0.1, 0.05, 0.02, 0.01, 0.01, 0.01, 0.02, 0.05 };

    err->setCoefficients(x0, y0, a, c, 8);

    test("test1", err, 0);
}

void
test2(Error *err)
{

    double x0[] = { -3, -1, -2, -1, 1, 2, 2, 3, 1, 2, 3, 4, 5, 6, 7 };
    double y0[] = { -3, -3, 3, -1, 1, -3, 1, 3, 1, 2, 3, 4, 1, 2, 3 };
    double a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 1, 2, 3 };
    double c[] = { 0.1, 0.05, 0.02, 0.01, 0.02, 0.01, 0.02, 0.05, 1, 1, 1, 1, 1, 2, 3 };

    err->setCoefficients(x0, y0, a, c, 15);

    test("test2", err, 357.729);
}

void
test3(Error *err)
{

    double x0[] = { -3, -1, -2, -1, 1, 2, 2, 3, -3, -1, -2, -1, 1, 2, 2, 3, 1, 2, 3, 4, 5, 6, 7, -3, -1, -2, -1, 1, 2, 2, 3, 1, 2, 3, 4, 5, 6, 7 };
    double y0[] = { -3, -3, 3, -1, 1, -3, 1, 3, -3, -1, -2, -1, 1, 2, 2, 3, 1, 2, 3, 4, 5, 6, 7, -3, -1, -2, -1, 1, 2, 2, 3, 1, 2, 3, 4, 5, 6, 7 };
    double *a = new double[38];
    double *c = new double[38];

    for (int i = 0; i < 38; i++) {
        a[i] = 1 + i / 38.0;
        c[i] = 2 + i / 38.0;
    }

    err->setCoefficients(x0, y0, a, c, 38);

    test("test3", err, 2975.86);
}

void
test4(Error *err)
{

    double *x0 = new double[150];
    double *y0 = new double[150];
    double *a = new double[150];
    double *c = new double[150];

    for (int i = 0; i < 150; i++) {
        x0[i] = 5 - i * 0.2;
        a[i] = 1 + i / 8.0;
        y0[i] = 2 - i * 0.22;
        c[i] = 2 + i / 38.0;
    }

    err->setCoefficients(x0, y0, a, c, 150);

    test("test4", err, 3303.04);
}

void
testL(Error *err)
{
    double *x0;
    double *y0;
    double *a;
    double *c;

    if (glob_rank == 0)
        cout << "Test pozwalajacy na oszacowanie przyspieszenia" << endl;

    x0 = new double[111];
    y0 = new double[111];
    a = new double[111];
    c = new double[111];

    for (int i = 0; i < 111; i++) {
        x0[i] = 5 - i * 0.2;
        a[i] = 1 + i / 1.1;
        y0[i] = 2 - i * 0.4;
        c[i] = 2 + i / 38.0;
    }

    double toterror = 0;
    double error;

    for (int i = 0; i < 20; i++) {
        a[i] = i;
        err->setCoefficients(x0, y0, a, c, 111 - i * 3);

        err->calcError(opt_mpi);

        error = err->getError();
        dbgprt(2,"testL: POST error=%g\n",error);

        if (glob_rank == 0) {
            toterror += error;
        }
    }

    if (glob_rank == 0)
        cout << "Uwaga: ta wartosc nie moze zalezec od liczby uzytych procesow = " << toterror << endl;
}

int
main(int ac, char **av)
{
    char *cp;

    MPI_Init(&ac, &av);

    MPI_Comm_rank(MPI_COMM_WORLD, &glob_rank);

#if 1
    ranseed = 123767832;
#else
    ranseed = 0;
#endif

    --ac;
    ++av;

    for (;  ac > 0;  --ac, ++av) {
        if (glob_rank != 0)
            break;

        cp = *av;
        if (*cp != '-')
            break;

        switch (cp[1]) {
        case 'd':
            opt_debug = 1;
            break;

        case 'M':
            opt_mpi = 1;
            break;

        case 'R':
            ranseed = atoi(cp + 2);
            break;

        case 'S':
            opt_send = 1;
            break;
        }
    }

    tvzero = tvgetf();

    if (ranseed == 0)
        ranseed = time(NULL);
    if (glob_rank == 0) {
        cout << "Random: " << ranseed << std::endl;
        cout << "Mpi: " << opt_mpi << std::endl;
        cout << "Send: " << opt_send << std::endl;
        cout << "Debug: " << opt_debug << std::endl;
    }

    MPI_Bcast(&tvzero, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
    MPI_Bcast(&ranseed, 1, MPI_INT, 0, MPI_COMM_WORLD);
    MPI_Bcast(&opt_mpi, 1, MPI_INT, 0, MPI_COMM_WORLD);
    MPI_Bcast(&opt_send, 1, MPI_INT, 0, MPI_COMM_WORLD);
    MPI_Bcast(&opt_debug, 1, MPI_INT, 0, MPI_COMM_WORLD);

    Error *err = new Error();
    double *v;

    v = new double[SIZE];
    double x0[] = { -3, -2, -2, -1, 1, 2, 2, 3 };
    double y0[] = { -3, -3, 3, -1, 1, -3, 3, 3 };
    double a[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
    double c[] = { 0.1, 0.05, 0.02, 0.01, 0.01, 0.01, 0.02, 0.05 };

    generate(v, x0, y0, a, c);
    shuffle(v);

    // udostepniam dane dla procesu = 0
    err->setValues(v, LEN, MULT);

    test1(err);
    test2(err);
    test3(err);
    test4(err);
    test1(err);

    testL(err);

    MPI_Finalize();

    return 0;
}

double
tvgetf(void)
{
    struct timespec ts;
    double sec;

    clock_gettime(CLOCK_REALTIME,&ts);

    sec = ts.tv_nsec;
    sec /= 1e9;
    sec += ts.tv_sec;

    return sec;
}

int
dbgif(int lvl)
{
    int goflg = 0;

    do {
        if (! opt_debug)
            break;

        if (glob_rank == 0) {
            goflg = 1;
            break;
        }

        if (lvl > opt_debug)
            goflg = 1;
    } while (0);

    return goflg;
}

void
_dbgprt(const char *fmt,...)
{
    va_list ap;
    double tvnow;

    tvnow = tvgetf();
    tvnow -= tvzero;

    printf("%.9f/%d ",tvnow,glob_rank);

    va_start(ap,fmt);
    vprintf(fmt,ap);
    va_end(ap);
}

<强>更新

我已更新上面的完整代码示例以添加跟踪。它似乎适用于所有情况。

然而,要回答你的后续问题......

  

如果我无法修改主类怎么办?

我认为你的意思是&#34;主要文件&#34;其中包括test*函数? 否。您必须修改test*功能,因为它们是您所拥有的错误的来源。

  

如果我只需要修改Error,那么如果我从这些循环中移出MPI_reduce并在你建议的if语句的末尾添加error = error_p,它会工作吗?

大多数情况下,但您需要error_p = error。在我的原始帖子中重读我对此的评论,并重新阅读MPI_Reduce的文档。第一个arg是客户端发送给root的,第二个arg是root获取值的方式。

  

有趣的是,我也无法播放length_2。

那是因为您使用的是MPI_INT而不是MPI_DOUBLE

  

然后我想我必须添加所有这些Bcast,但即使我广播了大小值,我也不知道如何为其余的数组设置Bcast函数的第二个参数。我尝试的一切都以分段错误结束。

需要广播x0等。人。数组如果始终将它们设置为test *,无论等级。请记住,我提到计算量足够小,可以在所有等级中重复

所有中设置 错误。

  

正如预期的那样,无论我尝试什么,我都无法将任何数组广播到剩余的进程中。

广播部分正常工作(即0级正确发送数据)。但是,非根级别没有地方可以存储它,而且你会得到段错误。

这是您的test4

void
test4(Error * err, int rank)
{

    if (rank == 0) {
        double *x0 = new double[150];
        double *y0 = new double[150];
        double *a = new double[150];
        double *c = new double[150];

        for (int i = 0; i < 150; i++) {
            x0[i] = 5 - i * 0.2;
            a[i] = 1 + i / 8.0;
            y0[i] = 2 - i * 0.22;
            c[i] = 2 + i / 38.0;
        }

        err->setCoefficients(x0, y0, a, c, 150);
    }

    test(err, 3303.04, rank);
}

以下是修改后的版本:

void
test4(Error * err, int rank)
{
    double *x0 = new double[150];
    double *y0 = new double[150];
    double *a = new double[150];
    double *c = new double[150];

    for (int i = 0; i < 150; i++) {
        x0[i] = 5 - i * 0.2;
        a[i] = 1 + i / 8.0;
        y0[i] = 2 - i * 0.22;
        c[i] = 2 + i / 38.0;
    }

    err->setCoefficients(x0, y0, a, c, 150);

    test(err, 3303.04, rank);
}

在我的版本中,setCoefficients 总是被调用。因此,非根等级Error总是具有x0等的有效[非空]指针。

在您的版本中,setCoefficients仅为 调用根级别。因此,非根等级将总是具有x0等的空指针。因此,您将获得段错误。

请注意,如果您使用-Wall进行了编译,则某些test*函数会被标记为可能未初始化的值的警告。修复这些可能会让你更早地解决问题。

至少,即使您希望播放x0等。 al。,你必须仍然确保子级别分配有效/足够的空间。

要实现这一目标,请修改test4

void
test4(Error * err, int rank)
{

    if (rank == 0) {
        double *x0 = new double[150];
        double *y0 = new double[150];
        double *a = new double[150];
        double *c = new double[150];

        for (int i = 0; i < 150; i++) {
            x0[i] = 5 - i * 0.2;
            a[i] = 1 + i / 8.0;
            y0[i] = 2 - i * 0.22;
            c[i] = 2 + i / 38.0;
        }

        err->setCoefficients(x0, y0, a, c, 150);
    }
    else
        err->setBuffers(150);

    test(err, 3303.04, rank);
}

而且,你需要类似的东西:

void
Error::setBuffers(int size)
{

    this->x0 = realloc(this->x0,sizeof(double) * size);
    this->y0 = realloc(this->y0,sizeof(double) * size);
    this->a = realloc(this->a,sizeof(double) * size);
    this->c = realloc(this->c,sizeof(double) * size);

    this->size = size;
}

因此,在完成所有这些后,您最终得到了一个更复杂的解决方案,而仍然必须修改test*

当然,在calcError的MPI版本中,您想要这样做:

MPI_Bcast(x0, size, MPI_DOUBLE, 0, MPI_COMM_WORLD);