我通常同意 Shawn Chin 关于使用现有库进行文件读取的观点;在这种情况下,我可能不同意,因为文件格式非常简单,而且 MPI 知道数据在内存中的布局方式非常重要。分配为 nxm 的连续 1-d 数组的 2d nxm 数组与分散在内存中的行非常不同!与往常一样,这是 C 的错,因为没有真正的多维数组。另一方面,您可以查看 libnetpbm 库并查看它是如何分配的,或者按照 Shawn 的建议,在读入后将整个内容复制到连续的内存中。
还要注意,使用(二进制)P5 格式实际上会更容易,因为可以在开始时使用 MPI-IO 并行读取数据,而不是让一个处理器完成所有读取并使用 scatter/gather做数据分布。使用 ascii 文件,您永远无法真正知道记录的长度,这使得协调 I/O 变得非常困难。
另请注意,这实际上不是 2d 问题 - 您只是对数组的每一块进行元素操作。因此,您可以通过将数据视为一维数组并忽略几何图形来大大简化事情。如果您(例如)对图像应用 2d 过滤器,则情况并非如此,因为几何形状很重要,您必须相应地对数据进行分区;但在这里我们不在乎。
最后,即使在这种简单的情况下,您也必须使用 scatterv 和 gatherv,因为图像中的单元格数量可能不会除以 MPI 任务的数量。您可以通过填充数组以使其均匀划分来简化这里的逻辑;那么你可以在这里避免一些额外的步骤。
因此,如果您有一个read_pgm()
并且write_pgm()
知道将指针返回到单个连续的内存块中,则可以执行以下操作:
int main(int argc, char **argv) {
int ierr;
int rank, size;
int **greys;
int rows, cols, maxval;
int ncells;
int mystart, myend, myncells;
const int IONODE=0;
int *disps, *counts, *mydata;
int *data;
ierr = MPI_Init(&argc, &argv);
if (argc != 3) {
fprintf(stderr,"Usage: %s infile outfile\n",argv[0]);
fprintf(stderr," outputs the negative of the input file.\n");
return -1;
}
ierr = MPI_Comm_rank(MPI_COMM_WORLD, &rank);
ierr |= MPI_Comm_size(MPI_COMM_WORLD, &size);
if (ierr) {
fprintf(stderr,"Catastrophic MPI problem; exiting\n");
MPI_Abort(MPI_COMM_WORLD,1);
}
if (rank == IONODE) {
if (read_pgm(argv[1], &greys, &rows, &cols, &maxval)) {
fprintf(stderr,"Could not read file; exiting\n");
MPI_Abort(MPI_COMM_WORLD,2);
}
ncells = rows*cols;
disps = (int *)malloc(size * sizeof(int));
counts= (int *)malloc(size * sizeof(int));
data = &(greys[0][0]); /* we know all the data is contiguous */
}
/* everyone calculate their number of cells */
ierr = MPI_Bcast(&ncells, 1, MPI_INT, IONODE, MPI_COMM_WORLD);
myncells = ncells/size;
mystart = rank*myncells;
myend = mystart + myncells - 1;
if (rank == size-1) myend = ncells-1;
myncells = (myend-mystart)+1;
mydata = (int *)malloc(myncells * sizeof(int));
/* assemble the list of counts. Might not be equal if don't divide evenly. */
ierr = MPI_Gather(&myncells, 1, MPI_INT, counts, 1, MPI_INT, IONODE, MPI_COMM_WORLD);
if (rank == IONODE) {
disps[0] = 0;
for (int i=1; i<size; i++) {
disps[i] = disps[i-1] + counts[i-1];
}
}
/* scatter the data */
ierr = MPI_Scatterv(data, counts, disps, MPI_INT, mydata, myncells,
MPI_INT, IONODE, MPI_COMM_WORLD);
/* everyone has to know maxval */
ierr = MPI_Bcast(&maxval, 1, MPI_INT, IONODE, MPI_COMM_WORLD);
for (int i=0; i<myncells; i++)
mydata[i] = maxval-mydata[i];
/* Gather the data */
ierr = MPI_Gatherv(mydata, myncells, MPI_INT, data, counts, disps,
MPI_INT, IONODE, MPI_COMM_WORLD);
if (rank == IONODE) {
write_pgm(argv[2], greys, rows, cols, maxval);
}
free(mydata);
if (rank == IONODE) {
free(counts);
free(disps);
free(&(greys[0][0]));
free(greys);
}
MPI_Finalize();
return 0;
}