私は通常、既存のライブラリを使用してファイルを読み取ることについてShawnChinに同意します。この場合、ファイル形式が非常に単純であり、MPIがデータがメモリにどのように配置されているかを知ることが非常に重要であるため、私は同意しない可能性があります。nxmの連続する1次元配列として割り当てられた2次元のnxm配列は、メモリ全体に散在する行とは大きく異なります。いつものように、これは実際のマルチd配列を持たないことに対するCのせいです。一方、libnetpbmライブラリをチェックして、それがどのように割り当てられているかを確認するか、Shawnが示唆しているように、読み込んだ後にすべてを連続したメモリにコピーすることができます。
また、これは(バイナリ)P5形式の方が実際には簡単であることに注意してください。これは、1つのプロセッサですべての読み取りを実行し、スキャッタ/ギャザーを使用するのではなく、MPI-IOを使用してデータを最初から並列に読み取ることができるためです。データ配布を行います。ASCIIファイルでは、レコードの長さが実際にはわからないため、調整されたI/Oが非常に困難になります。
また、これは実際には2次元の問題ではないことに注意してください。つまり、配列のすべての部分に対して要素ごとの操作を実行しているだけです。したがって、データを1次元配列として扱い、ジオメトリを無視するだけで、作業を大幅に簡素化できます。これは、(たとえば)画像に2Dフィルターを適用する場合には当てはまりません。ジオメトリが重要であり、それに応じてデータを分割する必要があるためです。しかし、ここでは気にしません。
最後に、この単純なケースでも、画像内のセルの数がMPIタスクの数で均等に分割されない可能性があるため、scattervとgathervを使用する必要があります。配列をパディングして均等に分割するだけで、ここでロジックを単純化できます。次に、ここで余分な手順のいくつかを回避できます。
したがって、があり、ポインタを単一の連続したメモリブロックに返すことがわかっている場合は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;
}