我正在尝试用mpi执行分形图并行计算。 我将我的计划分为4部分:
步骤1和2正在工作,但是当我尝试将行发送到0级时,程序正在停止并阻止。我知道MPI_Send可以阻止在这里没有理由。
以下是第一步:
第1步:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Include the MPI library for function calls */
#include <mpi.h>
/* Define tags for each MPI_Send()/MPI_Recv() pair so distinct messages can be
* sent */
#define OTHER_N_ROWS_TAG 0
#define OTHER_PIXELS_TAG 1
int main(int argc, char **argv) {
const int nRows = 513;
const int nCols = 513;
const int middleRow = 0.5 * (nRows - 1);
const int middleCol = 0.5 * (nCols - 1);
const double step = 0.00625;
const int depth = 100;
int pixels[nRows][nCols];
int row;
int col;
double xCoord;
double yCoord;
int i;
double x;
double y;
double tmp;
int myRank;
int nRanks;
int evenSplit;
int nRanksWith1Extra;
int myRow0;
int myNRows;
int rank;
int otherNRows;
int otherPixels[nRows][nCols];
/* Each rank sets up MPI */
MPI_Init(&argc, &argv);
/* Each rank determines its ID and the total number of ranks */
MPI_Comm_rank(MPI_COMM_WORLD, &myRank);
MPI_Comm_size(MPI_COMM_WORLD, &nRanks);
printf("My rank is %d \n",myRank);
evenSplit = nRows / nRanks;
nRanksWith1Extra = nRows % nRanks;
/*Each rank determine the number of rows that he will have to perform (well balanced)*/
if (myRank < nRanksWith1Extra) {
myNRows = evenSplit + 1;
myRow0 = myRank * (evenSplit + 1);
}
else {
myNRows = evenSplit;
myRow0 = (nRanksWith1Extra * (evenSplit + 1)) +
((myRank - nRanksWith1Extra) * evenSplit);
}
/*__________________________________________________________________________________*/
第2步:
/*_____________________PERFORM CALCUL ON EACH PIXEL________________________________ */
for (row = myRow0; row < myRow0 + myNRows; row++) {
/* Each rank loops over the columns in the given row */
for (col = 0; col < nCols; col++) {
/* Each rank sets the (x,y) coordinate for the pixel in the given row and
* column */
xCoord = (col - middleCol) * step;
yCoord = (row - middleRow) * step;
/* Each rank calculates the number of iterations for the pixel in the
* given row and column */
i = 0;
x = 0;
y = 0;
while ((x*x + y*y < 4) && (i < depth)) {
tmp = x*x - y*y + xCoord;
y = 2*x*y + yCoord;
x = tmp;
i++;
}
/* Each rank stores the number of iterations for the pixel in the given
* row and column. The initial row is subtracted from the current row
* so the array starts at 0 */
pixels[row - myRow0][col] = i;
}
//printf("one row performed by %d \n",myRank);
}
printf("work done by %d \n",myRank);
/*_________________________________________________________________________________*/
第3步:
/*__________________________SEND DATA TO RANK 0____________________________________*/
/* Each rank (including Rank 0) sends its number of rows to Rank 0 so Rank 0
* can tell how many pixels to receive */
MPI_Send(&myNRows, 1, MPI_INT, 0, OTHER_N_ROWS_TAG, MPI_COMM_WORLD);
printf("test \n");
/* Each rank (including Rank 0) sends its pixels array to Rank 0 so Rank 0
* can print it */
MPI_Send(&pixels, sizeof(int)*myNRows * nCols, MPI_BYTE, 0, OTHER_PIXELS_TAG,
MPI_COMM_WORLD);
printf("enter ranking 0 \n");
/*_________________________________________________________________________________*/
第4步:
/*________________________TREAT EACH ROW IN RANK 0_________________________________*/
/* Only Rank 0 prints so the output is in order */
if (myRank == 0) {
/* Rank 0 loops over each rank so it can receive that rank's messages */
for (rank = 0; rank < nRanks; rank++){
/* Rank 0 receives the number of rows from the given rank so it knows how
* many pixels to receive in the next message */
MPI_Recv(&otherNRows, 1, MPI_INT, rank, OTHER_N_ROWS_TAG,
MPI_COMM_WORLD, MPI_STATUS_IGNORE);
/* Rank 0 receives the pixels array from each of the other ranks
* (including itself) so it can print the number of iterations for each
* pixel */
MPI_Recv(&otherPixels, otherNRows * nCols, MPI_INT, rank,
OTHER_PIXELS_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
/* Rank 0 loops over the rows for the given rank */
for (row = 0; row < otherNRows; row++) {
/* Rank 0 loops over the columns within the given row */
for (col = 0; col < nCols; col++) {
/* Rank 0 prints the value of the pixel at the given row and column
* followed by a comma */
printf("%d,", otherPixels[row][col]);
}
/* In between rows, Rank 0 prints a newline character */
printf("\n");
}
}
}
/* All processes clean up the MPI environment */
MPI_Finalize();
return 0;
}
我想了解为什么会阻止,你能解释一下吗? 我是MPI的新用户,我想学习的不仅仅是让程序正常运行。
提前谢谢。
答案 0 :(得分:1)
当您在发送到等级0本身时使用阻塞发送/接收构造时,可能会导致死锁。
来自MPI 3.0 standard, Section 3.2.4:
Source = destination是允许的,也就是说,进程可以向自己发送消息。 (但是,使用上述阻塞发送和接收操作这样做是不安全的, 因为这可能会导致僵局。见3.5节。)
可能的解决方案:
答案 1 :(得分:1)
MPI_Send
定义标准阻止操作。
请注意,阻止意味着:
在消息数据和信封已安全存储之前,它不会返回,以便发送方可以自由修改发送缓冲区。消息可能会直接复制到匹配的接收缓冲区中,也可能会被复制到临时系统缓冲区中。
尝试使用MPI_Send
和MPI_Recv
向自己发送排名是一个死锁。
您的情况的惯用模式是使用适当的集体沟通操作MPI_Gather
和MPI_Gatherv
。
答案 2 :(得分:1)
正如之前的回答中所述,MPI_Send()
可能阻止。
从理论上MPI
的角度来看,您的应用程序是不正确的,因为在没有发布接收时可能会出现死锁(等级0
MPI_Send()
。
从非常务实的角度来看,MPI_Send()
通常会在发送小消息时立即返回(例如myNRows
),但会阻止,直到发布匹配的接收为止发送大消息时(例如pixels
)。请记住
MPI
角度来看,假设MPI_Send()
会立即返回small
条消息如果您确实希望确保应用程序无死锁,则只需将MPI_Send()
替换为MPI_Ssend()
。
回到你的问题,这里有几个选项
0
不与自身通信(所有信息都可用,因此无需通信MPI_Irecv()
之前发布MPI_Send()
,并将MPI_Recv(source=0)
替换为MPI_Wait()
0
不是MPI_Send()
也不是MPI_Recv(source=0)
,而是MPI_Sendrecv
。这是我推荐的选项,因为您只需对通信模式进行一些小改动(计算模式保持不变),这是更优雅的imho。