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以外的进程处理,但由于他们看不到a[]包含什么值,所以它不起作用。我尝试了MPI_Bcast(a,?,MPI_DOUBLE,0,MPI_COMM_WORLD),但我无法获得大小的值,因为它与每个方法调用都不同。整个代码有点大,但如果有人想看一看,我可以在某个地方发布。。。

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

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

划分循环很好——只是那些编译不起作用,我不知道如何使这些过程相互通信。

为什么不先广播要广播的数组的大小?

你真的很接近。我相信我能够更正程序,更正后的文本如下。

注意:我在真正修改程序之前就开始写这个解释了,所以把"你必须做"替换为"我做了">

我能够下载并构建您的程序。通过将Function::value更改为Function_value(一个全局函数),我能够在现有的基础上解决缺失的Function.h

所做的必须做的一件事是让秩0获得随机种子值。它必须首先广播,这样客户端列组才能获得它,这样他们对shuffle的调用将产生相同的结果。因此,我创建了一个全局ranseed,从main中的bcast设置,以便shuffle可以看到它

我将test*例程更改为始终计算x0y0ac,而不考虑秩。否则,编译器会抱怨这些指针的值可能未初始化。计算量很小。无需广播这些值。如果这种预先计算是密集的(事实并非如此),那么预测阵列将是可行的。

我想您已经在calcError的MPI版本中实现了这一点,因为您注释掉了这些数组的bcast。

AFAICT,您的MPIcalcError看起来不错,除了有两件事:

(1)MPI_Reduce调用应该而不是if (myid_c >= 0)块内。我认为它需要在这个区块之后的底部(即,即使排名为0也需要调用它)。

(2) 在MPI_Reduce位置[在if]中,我认为在循环的双层之后需要一个error_p = error;,因为error_p是客户端发送的值[而error是根接收的值]

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

我使用mpi模式运行了程序:mpirun -np 8 <program> -M,所有测试似乎都能工作。过了一段时间,我在testL上做了ctrl-c,所以不能保证。


这是一个单独的[存档]文件,类似于您的粘贴,文件分隔符行是% <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=%dn",
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=%gn",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=%gn",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=%gn",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);
}

更新:

我已经更新了上面的完整代码示例,添加了跟踪。它似乎在所有情况下都有效。

然而,为了回答您的后续问题。。。

如果我不能修改主类怎么办?

我想你指的是包括test*函数的"主文件">您必须修改test*函数,因为它们是您遇到的错误的来源。

如果我只能修改Error,如果我从这些循环中移出MPI_reduce,并按照您的建议在If语句的末尾添加error = error_p,它会起作用吗?

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

很有趣,我也不能广播length_2。

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

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

如果总是在测试*中设置它们,则不需要广播x0等数组,而的秩为。请记住,我提到的计算足够小,可以在所有等级的中重复

将它们设置在所有列中错误。

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

广播部分工作正常(即秩0正确发送数据)。但是,非根列没有地方存储它,并且您会遇到segfault。

这是您的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等的空指针。因此,您将得到一个segfault。

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

至少,即使您希望广播x0等,也必须确保子列组分配了有效/足够的空间。

为了实现这一点,这里有一个修改的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);

最新更新