Откройте MPI для распределения и манипулирования 2d массивом в файлах PGM - PullRequest
1 голос
/ 22 февраля 2011

Мне нужно использовать Open MPI для распределения 2d-массива в файле PGM между 10 работающими компьютерами.Затем мне нужно манипулировать каждым значением массива, чтобы получить негативное изображение (255-я), а затем распечатать вывод обратно.Я думаю об использовании mpi_scatter и mpi_gather для распространения данных.Теперь проблема заключается в том, как прочитать 2-й массив в подмассив и отправить подмассив каждому рабочему компьютеру для выполнения манипуляции.Я пишу эту программу на языке C.

Может кто-нибудь может помочь мне решить эту проблему или дать идею?Спасибо.

Ниже приведен пример массива в файле PGM:

P2
# created by 'xv balloons_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 ]

1 голос
/ 22 февраля 2011

Простейший способ прочитать файл 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];

Теперь мы готовы к

  1. буфер рассеяния по узлам
  2. вам может потребоваться передать maxval каждому узлу.
  3. каждый узел выполняет buffer[n] = maxval - buffer[n];
  4. собрать буфер обратно на мастер
  5. реконструировать выходное изображение

Вы можете восстановить изображение, записав его обратно вам image данные, или просто распечатать файл pgm вручную, если вы знакомы с форматом

Что касается типов данных, используемых для операций MPI, MPI_UNSIGNED будет работать, поскольку gray - это typedef, равный unsigned int. Тем не менее, для обеспечения прямой совместимости вы можете использовать MPI_BYTE и умножить send_count на sizeof(gray).

не использует libpgm

Если вы хотите прочитать файлы вручную, это не так уж сложно, поскольку ваш файл PGM находится в простом формате (P2 вместо P5).

Предполагая, что формат действителен, вам необходимо:

  1. Открыть файл
  2. Пропустить первые 2 строки
  3. Читается в столбцах и строках: fscanf(fp,"%d %d", &cols, &rows);
  4. Читайте в maxval: fscanf(fp,"%d", &maxval);
  5. Выделите свой буфер в соответствии с cols и rows
  6. Прочитать в остальной части изображения, циклически перебирая столбцы / строки и повторяя fscanf(fp,"%d", &buffer[r][c]);
0 голосов
/ 24 февраля 2011

Я бы обычно соглашался с Шоном Чином в использовании существующих библиотек для чтения файлов; в этом случае я могу не согласиться, потому что формат файла очень прост, и для 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;
}
...