Я бы обычно соглашался с Шоном Чином в использовании существующих библиотек для чтения файлов; в этом случае я могу не согласиться, потому что формат файла очень прост, и для MPI так важно знать, как данные размещаются в памяти. 2d массив nxm, выделенный как непрерывный 1-й массив nxm, сильно отличается от строк, разбросанных по всей памяти! Как всегда, в этом виноват С, поскольку он не имеет настоящих многомерных массивов. С другой стороны, вы можете проверить библиотеки libnetpbm и посмотреть, как они распределяются, или, как предлагает Шон, скопировать все это в непрерывную память после прочтения.
Также обратите внимание, что это на самом деле было бы проще с (двоичным) форматом P5, поскольку можно было бы использовать MPI-IO для чтения данных параллельно в самом начале, вместо того, чтобы один процессор делал все чтение и использовал разброс / собрать, чтобы сделать распределение данных. С файлами ascii вы никогда не знаете, как долго будет длиться запись, что очень затрудняет скоординированный ввод-вывод.
Также обратите внимание, что это действительно не двумерная проблема - вы просто делаете поэлементную операцию над каждым фрагментом массива. Таким образом, вы можете значительно упростить задачу, просто обрабатывая данные как массив 1d и игнорируя геометрию. Это не было бы в том случае, если бы вы (скажем) применили 2d-фильтр к изображению, поскольку там имеет значение геометрия, и вам придется соответствующим образом разделять данные; но здесь нам все равно.
Наконец, даже в этом простом случае вы должны использовать scatterv и collectv, потому что количество ячеек в изображении может не делиться равномерно на количество задач 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;
}