1

我需要使用 Open MPI 在 10 台工作计算机之间分配 PGM 文件中的二维数组。然后我需要操纵数组的每个值以获得负图像(255-i),然后将输出打印回来。我正在考虑使用mpi_scattermpi_gather分发数据。现在的问题是如何将二维数组读入子数组并将子数组发送到每个工作计算机进行操作。我正在用 C 语言编写这个程序。

谁能帮我解决这个问题或给出一个想法?谢谢你。

以下是 PGM 文件中的数组示例:

P2
# 由“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
4

2 回答 2

1

读取 PGM 文件的最简单方法是使用netpbm包中的libpgm 。

您使用以下方式读取 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因为grayunsigned int. 但是,为了严格向前兼容,您可以使用MPI_BYTE并乘以您send_count的 by sizeof(gray)

不使用 libpgm

如果您想手动读取文件,这并不难,因为您的 PGM 文件是纯格式(P2而不是P5)。

假设格式有效,您需要:

  1. 打开文件
  2. 跳过前 2 行
  3. 读入 cols 和 rows :fscanf(fp,"%d %d", &cols, &rows);
  4. 读入 maxval :fscanf(fp,"%d", &maxval);
  5. 根据cols和分配你的缓冲区rows
  6. 通过遍历 col/rows 并重复读取图像的其余部分fscanf(fp,"%d", &buffer[r][c]);
于 2011-02-22T12:25:16.687 回答
0

我通常同意 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;
}
于 2011-02-24T14:33:20.220 回答