MPI - 将部分图像发送到不同的进程
MPI - sending parts of image to different processes
我正在编写一个程序,其中进程 0 将部分图像发送到其他进程,这些进程转换(长时间操作)这部分并发送回等级 0。我有一个问题。为了重现我的问题,我写了一个简单的例子。大小为 512x512px 的图像被进程 0 分成 4 个部分(垂直条纹)。接下来其他进程将这部分保存在磁盘上。问题是每个进程保存相同的部分。我发现图像在部分上正确分割,但问题可能出在发送数据上。我的代码有什么问题?
运行:
mpirun -np 5 ./example
主要:
int main(int argc, char **argv) {
int size, rank;
MPI_Request send_request, rec_request;
MPI_Status status;
ostringstream s;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
if (rank == 0) {
Mat mat = imread("/home/user/original.jpg", CV_LOAD_IMAGE_COLOR);
if (!mat.data) exit(-1);
int idx = 1;
for (int c = 0; c < 512; c += 128) {
Mat slice = mat(Rect(c, 0, 128, 512)).clone();
MPI_Isend(slice.data, 128 * 512 * 3, MPI_BYTE, idx, 0, MPI_COMM_WORLD, &send_request);
idx++;
}
}
if (rank != 0) {
Mat test = Mat(512, 128, CV_8UC3);
MPI_Irecv(test.data, 128 * 512 * 3, MPI_BYTE, 0, 0, MPI_COMM_WORLD, &rec_request);
MPI_Wait(&rec_request, &status);
s << "/home/user/p" << rank << ".jpg";
imwrite(s.str(), test);
}
MPI_Finalize();
return 0;
}
问题是您没有等到发送操作完成才销毁矩阵 Mat
。使用 MPI_Send
而不是 MPI_Isend
.
如果您真的想使用非阻塞通信,则必须跟踪所有 MPI_Request 个对象和所有 Mat
个图像,直到发送完成。
如果你坚持使用非阻塞操作,那么同时发出多个它们的正确方法是:
MPI_Request *send_reqs = new MPI_Request[4];
int idx = 1;
for (int c = 0; c < 512; c += 128) {
Mat slice = mat(Rect(c, 0, 128, 512)).clone();
MPI_Isend(slice.data, 128 * 512 * 3, MPI_BYTE, idx, 0, MPI_COMM_WORLD, &send_reqs[idx-1]);
idx++;
}
MPI_Waitall(4, send_reqs, MPI_STATUSES_IGNORE);
delete [] send_reqs;
另一个(恕我直言,更好)选项是利用 MPI_Scatterv
分散原始数据缓冲区。因此,您甚至可以保存图像矩阵的克隆部分。
if (rank == 0) {
Mat mat = imread("/home/user/original.jpg", CV_LOAD_IMAGE_COLOR);
if (!mat.data) exit(-1);
int *send_counts = new int[size];
int *displacements = new int[size];
// The following calculations assume row-major storage
for (int i = 0; i < size; i++) {
send_counts[i] = displacements[i] = 0;
}
int idx = 1;
for (int c = 0; c < 512; c += 128) {
displacements[idx] = displacements[idx-1] + send_counts[idx-1];
send_counts[idx] = 128 * 512 * 3;
idx++;
}
MPI_Scatterv(mat.data, send_counts, displacements, MPI_BYTE,
NULL, 0, MPI_BYTE, 0, MPI_COMM_WORLD);
delete [] send_counts;
delete [] displacements;
}
if (1 <= rank && rank <= 4) {
Mat test = Mat(512, 128, CV_8UC3);
MPI_Scatterv(NULL, NULL, NULL, MPI_BYTE,
test.data, 128 * 512 * 3, MPI_BYTE, 0, MPI_COMM_WORLD);
s << "/home/user/p" << rank << ".jpg";
imwrite(s.str(), test);
}
注意 MPI_Scatterv
的参数是如何准备的。由于您只分散到 4 个 MPI 进程,将 send_counts[]
的某些元素设置为零允许程序在超过 5 个 MPI 进程的情况下正常运行。此外,原始代码中的根排名不会发送给自身,因此 send_counts[0]
必须为零。
我正在编写一个程序,其中进程 0 将部分图像发送到其他进程,这些进程转换(长时间操作)这部分并发送回等级 0。我有一个问题。为了重现我的问题,我写了一个简单的例子。大小为 512x512px 的图像被进程 0 分成 4 个部分(垂直条纹)。接下来其他进程将这部分保存在磁盘上。问题是每个进程保存相同的部分。我发现图像在部分上正确分割,但问题可能出在发送数据上。我的代码有什么问题?
运行:
mpirun -np 5 ./example
主要:
int main(int argc, char **argv) {
int size, rank;
MPI_Request send_request, rec_request;
MPI_Status status;
ostringstream s;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
if (rank == 0) {
Mat mat = imread("/home/user/original.jpg", CV_LOAD_IMAGE_COLOR);
if (!mat.data) exit(-1);
int idx = 1;
for (int c = 0; c < 512; c += 128) {
Mat slice = mat(Rect(c, 0, 128, 512)).clone();
MPI_Isend(slice.data, 128 * 512 * 3, MPI_BYTE, idx, 0, MPI_COMM_WORLD, &send_request);
idx++;
}
}
if (rank != 0) {
Mat test = Mat(512, 128, CV_8UC3);
MPI_Irecv(test.data, 128 * 512 * 3, MPI_BYTE, 0, 0, MPI_COMM_WORLD, &rec_request);
MPI_Wait(&rec_request, &status);
s << "/home/user/p" << rank << ".jpg";
imwrite(s.str(), test);
}
MPI_Finalize();
return 0;
}
问题是您没有等到发送操作完成才销毁矩阵 Mat
。使用 MPI_Send
而不是 MPI_Isend
.
如果您真的想使用非阻塞通信,则必须跟踪所有 MPI_Request 个对象和所有 Mat
个图像,直到发送完成。
如果你坚持使用非阻塞操作,那么同时发出多个它们的正确方法是:
MPI_Request *send_reqs = new MPI_Request[4];
int idx = 1;
for (int c = 0; c < 512; c += 128) {
Mat slice = mat(Rect(c, 0, 128, 512)).clone();
MPI_Isend(slice.data, 128 * 512 * 3, MPI_BYTE, idx, 0, MPI_COMM_WORLD, &send_reqs[idx-1]);
idx++;
}
MPI_Waitall(4, send_reqs, MPI_STATUSES_IGNORE);
delete [] send_reqs;
另一个(恕我直言,更好)选项是利用 MPI_Scatterv
分散原始数据缓冲区。因此,您甚至可以保存图像矩阵的克隆部分。
if (rank == 0) {
Mat mat = imread("/home/user/original.jpg", CV_LOAD_IMAGE_COLOR);
if (!mat.data) exit(-1);
int *send_counts = new int[size];
int *displacements = new int[size];
// The following calculations assume row-major storage
for (int i = 0; i < size; i++) {
send_counts[i] = displacements[i] = 0;
}
int idx = 1;
for (int c = 0; c < 512; c += 128) {
displacements[idx] = displacements[idx-1] + send_counts[idx-1];
send_counts[idx] = 128 * 512 * 3;
idx++;
}
MPI_Scatterv(mat.data, send_counts, displacements, MPI_BYTE,
NULL, 0, MPI_BYTE, 0, MPI_COMM_WORLD);
delete [] send_counts;
delete [] displacements;
}
if (1 <= rank && rank <= 4) {
Mat test = Mat(512, 128, CV_8UC3);
MPI_Scatterv(NULL, NULL, NULL, MPI_BYTE,
test.data, 128 * 512 * 3, MPI_BYTE, 0, MPI_COMM_WORLD);
s << "/home/user/p" << rank << ".jpg";
imwrite(s.str(), test);
}
注意 MPI_Scatterv
的参数是如何准备的。由于您只分散到 4 个 MPI 进程,将 send_counts[]
的某些元素设置为零允许程序在超过 5 个 MPI 进程的情况下正常运行。此外,原始代码中的根排名不会发送给自身,因此 send_counts[0]
必须为零。