我正在尝试使用 MPI 库用 C 编写一个程序,其中主进程创建一个 2D 数组并将其行分配给其他进程。矩阵的维度p*p
,其中p
是进程的数量。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mpi.h>
int **createMatrix (int nrows, int ncols) {
int **matrix;
int h, i, j;
if (( matrix = malloc(nrows*sizeof(int*))) == NULL) {
printf("Malloc error");
exit(1);
}
for (h=0; h<nrows; h++) {
if (( matrix[h] = malloc( ncols * sizeof(int))) == NULL) {
printf("Malloc error 2");
exit(1);
}
}
for (i=0; i<ncols; i++) {
for (j=0; j<nrows; j++) {
matrix[i][j] = ((i*nrows) + j);
}
}
return matrix;
}
void printArray (int *row, int nElements) {
int i;
for (i=0; i<nElements; i++) {
printf("%d ", row[i]);
}
printf("n");
}
void printMatrix (int **matrix, int nrows, int ncols) {
int i;
for (i=0; i<nrows; i++) {
printArray(matrix[i], ncols);
}
}
int main (int argc, char **argv) {
if (MPI_Init(&argc, &argv) != MPI_SUCCESS) {
perror("Error initializing MPI");
exit(1);
}
int p, id;
MPI_Comm_size(MPI_COMM_WORLD, &p); // Get number of processes
MPI_Comm_rank(MPI_COMM_WORLD, &id); // Get own ID
int **matrix;
if (id == 0) {
matrix = createMatrix(p, p); // Master process creates matrix
printf("Initial matrix:n");
printMatrix(matrix, p, p);
}
int *procRow = malloc(sizeof(int) * p); // received row will contain p integers
if (procRow == NULL) {
perror("Error in malloc 3");
exit(1);
}
if (MPI_Scatter(*matrix, p, MPI_INT, // send one row, which contains p integers
procRow, p, MPI_INT, // receive one row, which contains p integers
0, MPI_COMM_WORLD) != MPI_SUCCESS) {
perror("Scatter error");
exit(1);
}
printf("Process %d received elements: ", id);
printArray(procRow, p);
MPI_Finalize();
return 0;
}
运行此代码时收到的输出是
$ mpirun -np 4 test
Initial matrix:
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
Process 0 received elements: 0 1 2 3
Process 1 received elements: 1 50 32 97
Process 2 received elements: -1217693696 1 -1217684120 156314784
Process 3 received elements: 1 7172196 0 0
进程 0 显然收到正确的输入,但其他进程显示的数字我无法理解。另请注意,进程 1 和进程 3 的数量在程序的多次运行中是一致的,而进程 2 的数量在每次运行中都会发生变化。
在我看来,我的内存分配或指针使用有问题,但我对 C 编程很陌生。其次,显然,我也对如何解决我的问题感兴趣:)提前感谢!
我认为您从根本上误解了分散操作的作用以及MPI期望如何分配和使用内存。
MPI_Scatter
获取源数组并将其拆分为多个部分,向 MPI 通信器的每个成员发送一个唯一的部分。在您的示例中,您需要将矩阵作为线性内存中连续p*p
元素的分配,这会将p
值发送到每个进程。您的源"矩阵"是一个指针数组。不能保证行在内存中按顺序排列,MPI_Scatter
不知道如何遍历已传递它的指针数组。因此,调用只是读取通过矩阵指针间接传递的第一行末尾之外的内容,将内存中该行后面的任何内容视为数据。这就是为什么在第一行之后接收数据的进程中获取垃圾值的原因。
所有 MPI 数据复制例程都期望源内存和目标内存是"平面"线性阵列。多维 C 数组应该以行大顺序存储,而不是像您在此处所做的那样存储在指针数组中。一个廉价而令人讨厌的示例来说明分散调用正常工作的技巧是这样的:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mpi.h>
int *createMatrix (int nrows, int ncols) {
int *matrix;
int h, i, j;
if (( matrix = malloc(nrows*ncols*sizeof(int))) == NULL) {
printf("Malloc error");
exit(1);
}
for (h=0; h<nrows*ncols; h++) {
matrix[h] = h+1;
}
return matrix;
}
void printArray (int *row, int nElements) {
int i;
for (i=0; i<nElements; i++) {
printf("%d ", row[i]);
}
printf("n");
}
int main (int argc, char **argv) {
if (MPI_Init(&argc, &argv) != MPI_SUCCESS) {
perror("Error initializing MPI");
exit(1);
}
int p, id;
MPI_Comm_size(MPI_COMM_WORLD, &p); // Get number of processes
MPI_Comm_rank(MPI_COMM_WORLD, &id); // Get own ID
int *matrix;
if (id == 0) {
matrix = createMatrix(p, p); // Master process creates matrix
printf("Initial matrix:n");
printArray(matrix, p*p);
}
int *procRow = malloc(sizeof(int) * p); // received row will contain p integers
if (procRow == NULL) {
perror("Error in malloc 3");
exit(1);
}
if (MPI_Scatter(matrix, p, MPI_INT, // send one row, which contains p integers
procRow, p, MPI_INT, // receive one row, which contains p integers
0, MPI_COMM_WORLD) != MPI_SUCCESS) {
perror("Scatter error");
exit(1);
}
printf("Process %d received elements: ", id);
printArray(procRow, p);
MPI_Finalize();
return 0;
}
这样做:
$ mpicc -o scatter scatter.c
$ mpiexec -np 4 scatter
Initial matrix:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Process 0 received elements: 1 2 3 4
Process 1 received elements: 5 6 7 8
Process 2 received elements: 9 10 11 12
Process 3 received elements: 13 14 15 16
即,当您传递存储在线性内存中的数据时,它会起作用。等效的行主数组将静态分配,如下所示:
int matrix[4][4] = { { 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 },
{ 13, 14, 15, 16 } };
请注意静态分配的二维数组与代码动态分配的指针数组之间的区别。它们根本不是一回事,即使它们表面上看起来很相似。