gpt4 book ai didi

c - 创建鬼区 MPI 的正确方法 [光晕]

转载 作者:行者123 更新时间:2023-12-04 00:07:08 56 4
gpt4 key购买 nike

晚安

我正在参加并行编程类(class)。老师给了我们一个涉及模板计算的域划分的作业。对于这种类型的计算(有限差分),并行化代码的最常见方法是划分域并创建一些幽灵区域(光晕)。

为了更好地理解在 MPI 中创建幻影区域,我编写了这个简单的示例,初始化了一些内部值 = 123 和边界值 88 的数组。在所有通信结束时,所有幻影值应保持 8。在一个节点中,我'得到 123 个值。

连续剧(无鬼):

   123 - 123 - ... - 123 - 123

两个分区:

   123 - 123 - ... - 88  |||  88 - ... - 123 - 123

三个分区:

   123 - 123 - ... - 88  |||  88 - ... - 123 - 123 - 88 ||| 88 - ... - 123 - 123

除了这个错误,这里的主要问题是关于创建和维护更新的幽灵区域的正确方法。除了我凌乱的 if(myid == .... else if( myid = ... else 类型的实现之外,还有更干净的解决方案吗?人们通常如何实现这种并行性?

#include<mpi.h>
#include<stdio.h>
#include<stdlib.h>

int WhichSize(int mpiId, int numProc, int tam);

int main(int argc, char *argv[]){

int i;
int localSize;
int numProc;
int myid;

int leftProc;
int rightProc;

int * myArray;
int fullDomainSize = 16;

MPI_Request request;

MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &numProc);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);


// Lets get each partition size.
localSize = WhichSize(myid, numProc, fullDomainSize);


// Allocate arrays acording to proc number.
if(numProc == 1){

//printf("Allocating Array for serial usage\n");
myArray = (int*)malloc(localSize*sizeof(int));

} else if(numProc == 2) {

//printf("Allocating Array for 2 proc usage\n");
myArray = (int*)malloc((localSize+ 1)*sizeof(int));

} else if(numProc > 2) {

if (myid == 0 || myid == numProc - 1){

//printf("Allocating array for boundary nodes usage\n");
myArray = (int*)malloc((localSize+ 1)*sizeof(int));

} else {

//printf("Allocating array for inner nodes usage\n");
myArray = (int*)malloc((localSize+ 2)*sizeof(int));

}

}


// Now we will fill the arrays with a dummy value 123. For the
// boundaries (ghosts) we will fill than with 80. Just to differe
// ntiate.

if(numProc == 1){

//printf("----------------------------------------\n");
//printf("Filling the serial array with values... \n");

for (i = 0; i<localSize; i++){
myArray[i] = 123;
}

} else if(numProc == 2) {

////printf("------------------------------------------------\n");
//printf("Filling array for two proc usage with values... \n");

for (i = 0; i<localSize; i++){
myArray[i] = 123;
}

// ghost.
myArray[localSize+1] = 8;

} else if(numProc > 2) {

if (myid == 0 || myid == numProc - 1){

//printf("--------------------------------------------------\n");
//printf("Filling boundary node arrays usage with values... \n");

for (i = 0; i<localSize; i++){
myArray[i] = 123;
}

// ghosts.
myArray[localSize+1] = 8;

} else {

//printf("--------------------------------------------------\n");
//printf("Filling inner node arrays usage with values... \n");

for (i = 0; i<localSize; i++){
myArray[i] = 123;
}

// ghosts.
myArray[localSize+1] = 8;
myArray[0] = 8;

}

}


// Now lets comunicate the ghosts with MPI_Sendrecv().

if(numProc == 1){

//printf("Serial usage, no ghost to comunicate \n");

} else if(numProc == 2) {

if (myid == 0){

//printf("Sending ghost value from proc %d to %d\n", myid, myid + 1);
MPI_Isend(&myArray[localSize+1],
1,
MPI_INT,
1,
12345,
MPI_COMM_WORLD,
&request);

} else if (myid == 1) {

//printf("Receiving ghost value from proc %d to %d\n", myid-1, myid);
MPI_Irecv(&myArray[localSize+1],
1,
MPI_INT,
0,
12345,
MPI_COMM_WORLD,
&request);
}


} else if(numProc > 2) {

if (myid == 0){

rightProc = myid + 1;

if (myid == 0){

//printf("-------------------------------\n");
//printf("Communicating Boundary ghosts !\n");
//printf("-------------------------------\n");

//printf("Sending ghost value from proc %d to %d\n", myid, myid + 1);
MPI_Isend(&myArray[localSize+1],
1,
MPI_INT,
rightProc,
12345,
MPI_COMM_WORLD,
&request);

} else if (myid == rightProc) {

//printf("Receiving ghost value from proc %d to %d\n", myid-1, myid);
MPI_Irecv(&myArray[localSize+1],
1,
MPI_INT,
0,
12345,
MPI_COMM_WORLD,
&request);
}

} else if (myid == numProc - 1) {

leftProc = myid - 1;

if (myid == numProc - 1){

//printf("-------------------------------\n");
//printf("Communicating Boundary ghosts !\n");
//printf("-------------------------------\n");

////printf("Sending ghost value from proc %d to %d\n", myid, myid + 1);
MPI_Isend(&myArray[localSize+1],
1,
MPI_INT,
leftProc,
12345,
MPI_COMM_WORLD,
&request);

} else if (myid == leftProc) {

rightProc = myid + 1;

//printf("Receiving ghost value from proc %d to %d\n", myid-1, myid);
MPI_Irecv(&myArray[localSize+1],
1,
MPI_INT,
rightProc,
12345,
MPI_COMM_WORLD,
&request);
}

} else {

//printf("-------------------------------\n");
//printf("Communicating Inner ghosts baby\n");
//printf("-------------------------------\n");

leftProc = myid - 1;
rightProc = myid + 1;

// Communicate tail ghost.
if (myid == leftProc) {
MPI_Isend(&myArray[localSize+1],
1,
MPI_INT,
rightProc,
12345,
MPI_COMM_WORLD,
&request);

} else if (myid == rightProc){
MPI_Irecv(&myArray[localSize+1],
1,
MPI_INT,
leftProc,
12345,
MPI_COMM_WORLD,
&request);
}

// Communicate head ghost.
if (myid == leftProc) {
MPI_Isend(&myArray[0],
1,
MPI_INT,
rightProc,
12345,
MPI_COMM_WORLD,
&request);

} else if (myid == rightProc){
MPI_Irecv(&myArray[0],
1,
MPI_INT,
leftProc,
12345,
MPI_COMM_WORLD,
&request);
}
}
}


// Now I Want to see if the ghosts are in place !.

if (myid == 0){
printf("The ghost value is: %d\n", myArray[localSize + 1]);
} else if (myid == numProc - 1){
printf("The ghost value is: %d\n", myArray[0]);
} else {
printf("The head ghost is: %d\n", myArray[0]);
printf("The tail ghost is: %d\n", myArray[localSize + 1]);
}


MPI_Finalize();

exit(0);
}

int WhichSize(int mpiId, int numProc, int tam){

double resto;
int tamLocal;

tamLocal = tam / numProc;

resto = tam - tamLocal*numProc;

if (mpiId < resto) tamLocal = tamLocal + 1;


return tamLocal;
}

谢谢你们!

最佳答案

可以使用笛卡尔虚拟拓扑和发送-接收操作在 MPI 中优雅地实现光晕。

首先,条件运算符中有大量与等级相关的逻辑会使代码难以阅读和理解。当代码是对称的时会更好,即当所有等级执行相同的代码时。可以使用 MPI_PROC_NULL 空等级来处理极端情况 - 发送到该等级或从该等级接收会导致无操作。因此,这样做就足够了:

// Compute the rank of the left neighbour
leftProc = myid - 1;
if (leftProc < 0) leftProc = MPI_PROC_NULL;
// Compute the rank of the right neighbour
rightProc = myid + 1;
if (rightProc >= numProc) rightProc = MPI_PROC_NULL;

// Halo exchange in forward direction
MPI_Sendrecv(&myArray[localSize], 1, MPI_INT, rightProc, 0, // send last element to the right
&myArray[0], 1, MPI_INT, leftProc, 0, // receive into left halo
MPI_COMM_WORLD);
// Halo exchange in reverse direction
MPI_Sendrecv(&myArray[1], 1, MPI_INT, leftProc, 0, // send first element to the left
&myArray[localSize+1], 1, MPI_INT, rightProc, 0, // receive into right halo
MPI_COMM_WORLD);

该代码适用于任何等级,即使对于两端的等级 - 源或目标都是空等级,并且在相应方向上不会发生实际转移。它也适用于任意数量的 MPI 进程,从一个到多个。它要求所有等级的两边都有光环,包括那些并不真正需要它的(两个角落等级)。人们可以在那些虚拟光环中存储有用的东西,例如边界值(例如在求解偏微分方程时),或者干脆忍受通常可以忽略不计的内存浪费。

在您的代码中,您错误地使用了非阻塞操作。这些很棘手,需要小心。 MPI_Sendrecv 可以而且应该被使用。它同时执行发送和接收操作,从而防止死锁(只要每次发送都有匹配的接收)。

如果域是周期性的,那么秩计算逻辑就变得简单了:

// Compute the rank of the left neighbour
leftProc = (myid - 1 + numProc) % numProc;
// Compute the rank of the right neighbour
rightProc = (myid + 1) % numProc;

可以创建笛卡尔虚拟拓扑,然后使用 MPI_Cart_shift 来查找两个邻居的排名,而不是进行算术运算:

// Create a non-periodic 1-D Cartesian topology
int dims[1] = { numProc };
int periods[1] = { 0 }; // 0 - non-periodic, 1 - periodic
MPI_Comm cart_comm;
MPI_Cart_create(MPI_COMM_WORLD, 1, dims, periods, 1, &cart_comm);

// Find the two neighbours
MPI_Cart_shift(cart_comm, 0, 1, &leftProc, &rightProc);

光环交换的代码保持不变,唯一的区别是 cart_comm 应该替换 MPI_COMM_WORLDMPI_Cart_shift 自动处理极端情况,并在适当时返回 MPI_PROC_NULL。该方法的优点是您可以通过简单地翻转 periods[] 数组中的值轻松地在非周期域和周期域之间切换。

光环必须根据需要经常更新,这取决于算法。对于大多数迭代方案,更新必须在每次迭代开始时发生。可以通过引入多级光晕并使用外层的值来计算内层的值来降低通信频率。

总而言之,您的 main 函数可以简化为(不使用笛卡尔拓扑):

int main(int argc, char *argv[]){

int i;
int localSize;
int numProc;
int myid;

int leftProc;
int rightProc;

int * myArray;
int fullDomainSize = 16;

MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &numProc);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);

// Compute neighbouring ranks
rightProc = myid + 1;
if (rightProc >= numProc) rightProc = MPI_PROC_NULL;
leftProc = myid - 1;
if (leftProc < 0) leftProc = MPI_PROC_NULL;

// Lets get each partition size.
localSize = WhichSize(myid, numProc, fullDomainSize);

// Allocate arrays.
myArray = (int*)malloc((localSize+ 2)*sizeof(int));

// Now we will fill the arrays with a dummy value 123. For the
// boundaries (ghosts) we will fill than with 80. Just to differe
// ntiate.

//printf("--------------------------------------------------\n");
//printf("Filling node arrays usage with values... \n");

for (i = 1; i<localSize; i++){
myArray[i] = 123;
}

// ghosts.
myArray[localSize+1] = 8;
myArray[0] = 8;

//printf("-------------------------------\n");
//printf("Communicating Boundary ghosts !\n");
//printf("-------------------------------\n");

//printf("Sending ghost value to the right\n");
MPI_Sendrecv(&myArray[localSize], 1, MPI_INT, rightProc, 12345,
&myArray[0], 1, MPI_INT, leftProc, 12345,
MPI_COMM_WORLD);

//printf("Sending ghost value to the left\n");
MPI_Sendrecv(&myArray[1], 1, MPI_INT, leftProc, 12345,
&myArray[localSize+1], 1, MPI_INT, rightProc, 12345,
MPI_COMM_WORLD);

// Now I Want to see if the ghosts are in place !.

printf("[%d] The head ghost is: %d\n", myid, myArray[0]);
printf("[%d] The tail ghost is: %d\n", myid, myArray[localSize + 1]);

MPI_Finalize();

return 0;
}

关于c - 创建鬼区 MPI 的正确方法 [光晕],我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36960455/

56 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com