打开MPI以在PGM文件中分发和操作2d数组

我需要使用Open MPI在10台正在运行的计算机中分配PGM文件中的2darrays。 然后我需要操纵数组的每个值以获得负图像(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 

我通常会同意Shawn Chin关于使用现有库进行文件读取的问题; 在这种情况下,我可能不同意,因为文件格式非常简单,MPI知道如何在内存中布局数据非常重要。 分配为nxm的连续1-d数组的2d nxm数组与分散在整个内存中的行非常不同! 与往常一样,这是C没有真正的多维arrays的错误。 另一方面,您可以查看libnetpbm库并查看它是如何分配的,或者正如Shawn建议的那样,在读取之后将整个内容复制到连续的内存中。

另请注意,使用(二进制)P5格式实际上这更容易,因为可以使用MPI-IO在开头时并行读取数据,而不是让一个处理器执行所有读取并使用分散/聚集到做数据分发。 使用ascii文件,您永远不会知道记录的长度,这使得协调的I / O非常困难。

还要注意,这确实不是一个二维问题 – 你只是在每个数组上进行元素操作。 因此,只需将数据视为1d数组并忽略几何,就可以大大简化操作。 如果您(例如)对图像应用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 

读取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. 每个节点peforms buffer[n] = maxval - buffer[n];
  4. 将缓冲区收回到master
  5. 重建输出图像

您可以通过将图像写回image数据来重建图像,或者如果您熟悉格式 ,则只需手动打印出pgm文件

至于用于MPI操作的数据类型, MPI_UNSIGNED可以工作,因为grayunsigned int的typedef 。 但是,要严格向前兼容,您可以使用MPI_BYTE并将MPI_BYTE乘以sizeof(gray)

不使用libpgm

如果您想手动读取文件,那么由于您的PGM文件采用普通格式( P2而不是P5 ),因此实际上并不太难。

假设格式有效,您需要:

  1. 打开文件
  2. 跳过前两行
  3. 读入列和行: fscanf(fp,"%d %d", &cols, &rows);
  4. 读入maxval: fscanf(fp,"%d", &maxval);
  5. 根据colsrows分配缓冲区
  6. 通过循环col /行并重复fscanf(fp,"%d", &buffer[r][c]);读取图像的其余部分fscanf(fp,"%d", &buffer[r][c]);