Откройте MPI для распределения и манипулирования 2d массивом в файлах PGM
Мне нужно использовать Open MPI для распределения 2d-массива в файле PGM среди 10 рабочих компьютеров. Затем мне нужно манипулировать каждым значением массива, чтобы получить негативное изображение (255-я), а затем распечатать вывод обратно. Я думаю об использовании mpi_scatter
а также mpi_gather
распространять данные. Теперь проблема состоит в том, как прочитать 2-й массив в подмассив и отправить подмассив каждому рабочему компьютеру для выполнения манипуляции. Я пишу эту программу на C.
Может кто-нибудь может помочь мне решить эту проблему или дать идею? Спасибо.
Ниже приведен пример массива в файле PGM:
P2 # созданный 'xv balloonons_bw.tif' 640 480 255 232 227 220 216 212 209 207 206 205 205 205 207 208 209 210 211 212 211 211 213 212 211 210 209 210 210 211 212 211 210 210 210 210 211 210 210 210 210 209 210 209 208 209 208 209 210 209 208 210 209 209 208 208 208 209 208 208 208 207 207 207 206 207 207 207 207 207 207 207 207 207 207 205 204 206 205 205 204 204 204 203 202 203 202 201 201 201 200 199 199 200 199 198 198 198 197 197 198 197 196 195 195 194 193 192 192 191 191 190 190 190 190 189 189 190 188 188 188 187 187 187 186 186 186 186 187 186 186 187 188 188 187 186 186 186 185 186 186 186 187 186 186 186 185 185 187 186 185 186 185 185 186 185 184 185 186 185 186 186 186 185 186 185 185 185 184 183 184 184 183
2 ответа
Я бы обычно соглашался с Шоном Чином в использовании существующих библиотек для чтения файлов; в этом случае я могу не согласиться, потому что формат файла очень прост, и для MPI так важно знать, как данные размещаются в памяти. 2d массив nxm, выделенный как непрерывный 1-й массив nxm, сильно отличается от строк, разбросанных по всей памяти! Как всегда, в этом виноват С, поскольку он не имеет настоящих многомерных массивов. С другой стороны, вы можете проверить библиотеки libnetpbm и посмотреть, как они распределяются, или, как предлагает Шон, скопировать все это в непрерывную память после прочтения.
Также обратите внимание, что это было бы проще с (двоичным) форматом P5, поскольку можно было бы использовать MPI-IO для чтения данных параллельно в самом начале, вместо того, чтобы один процессор делал все чтение и использовал scatter / collect для сделать распределение данных. С файлами 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;
}
Простейший способ прочитать файл PGM - использовать libpgm из пакета netpbm.
Ваше чтение в файле pgm с использованием:
gray **image;
FILE *fp;
int cols; # num columns
int rows; # num rows
int maxval; # max grayscale value
fp = fopen("input.pgm","r");
image = pgm_readpgm( fp, &cols, &rows, &maxval);
Теперь вы можете получить негативное изображение, просматривая строки / столбцы:
for (i = 0; i < rows; i++)
for (j = 0; j < cols; j++)
image[i][j] = maxval - image[i][j];
Сложнее было бы распределить задачу по вашим узлам MPI как image
не может быть смежным в памяти (я не проверял). Можно было бы покопаться в коде, чтобы определить схему хранения и соответственно разбросать / собрать массивы, однако нет гарантии, что в будущем он не изменится (маловероятно, но возможно) и сломает ваш код.
Возможный, но неоптимальный способ сделать это состоит в том, чтобы создать временный буфер, который является непрерывным в памяти, распределить его и позже восстановить изображение. Например
gray *buffer = malloc(sizeof(gray) * rows * cols);
for (i = 0; i < rows; i++)
for (j = 0; j < cols; j++)
buffer[(i*cols)+j] = image[i][j];
Теперь мы готовы
- буфер рассеяния по узлам
- вам может понадобиться трансляция
maxval
к каждому узлу. - каждый узел формируется
buffer[n] = maxval - buffer[n];
- собрать буфер обратно на мастер
- восстановить выходное изображение
Вы можете восстановить изображение, написав его обратно вам image
данные, или просто распечатайте файл pgm вручную, если вы знакомы с форматом
Что касается типов данных для использования в операциях MPI, MPI_UNSIGNED
будет работать с gray
является определением типаunsigned int
, Тем не менее, чтобы быть строго совместимыми, вы можете использовать MPI_BYTE
и умножьте свой send_count
от sizeof(gray)
,
не использует libpgm
Если вы хотите прочитать файлы вручную, это не так уж сложно, поскольку ваш файл PGM находится в простом формате (P2
вместо P5
).
Предполагая, что формат действителен, вам необходимо:
- Открыть файл
- Пропустить первые 2 строки
- Читайте в столбцах и строках:
fscanf(fp,"%d %d", &cols, &rows);
- Читайте в maxval:
fscanf(fp,"%d", &maxval);
- Выделите свой буфер в соответствии с
cols
а такжеrows
- Прочитайте оставшуюся часть изображения, циклически перебирая столбцы / строки и повторяя
fscanf(fp,"%d", &buffer[r][c]);