我有几个初始化数组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
将循环划分得很好,只是那些不起作用的计算,而且我不确定如何让这些过程相互通信。
答案 0 :(得分:0)
为什么不广播您首先要播放的阵列的大小?
答案 1 :(得分:0)
你真的很亲密。我相信我能够纠正该程序,更正的文本在下面。
注意:在我实际修改程序之前,我开始写这个解释,所以替换&#34;你必须做&#34;用&#34;我做了&#34;
我能够下载并构建您的程序。通过将Function.h
更改为Function::value
[全局函数],我能够根据已有的Function_value
来解决这个问题。
你做要做的一件事就是让等级0得到随机种子值。它必须首先播放 ,因此客户端排名可以获得它,因此他们对shuffle
的调用将产生相同的结果。因此,我创建了一个全局ranseed
,来自main
中的bcast,shuffle
可以看到它。
我将test*
例程更改为始终计算x0
,y0
,a
和c
,无论等级如何。否则,编译器抱怨这些指针可能未初始化的值。计算量很小。无需广播这些值。如果这种预先计算是密集的[它不是这样的话],那么对阵列进行预测将是最佳选择。
我认为你已经在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> -M
上ctrl-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);