【问题标题】:MPI I/O, mix of single- and multiple-process outputMPI I/O,单进程和多进程输出的混合
【发布时间】:2018-04-20 00:00:39
【问题描述】:

我需要一个 MPI C 代码来通过 MPI I/O 将数据写入二进制文件。我需要进程 0 来编写一个短标题,然后我需要整个进程范围来编写它们自己的由标题指示的数组片段。然后我需要进程 0 来编写另一个标题,然后所有进程都编写下一个数组的片段,等等。我想出了以下测试代码,它实际上可以满足我的需求。没有人会比我更惊讶。

我的问题是,我是 MPI I/O 的新手。那我“明白”了吗?我这样做是“正确的方式”还是有一些更有效或更紧凑的方式来做到这一点?

代码是:(顺便说一句,如果您想对此进行测试,请仅使用 4 个 proc 进行尝试。)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "mpi.h"

#define ROWS 9
#define COLS 10

int main(int argc, char *argv[]) {

   int size_mpi, rank_mpi, row_mpi, col_mpi;
   int i,j,p,ttlcols;
   int sizes[]= {2*ROWS,2*COLS};
   int subsizes[]= {ROWS,COLS};
   int starts[] = {0,0};
   int vals[ROWS][COLS];
   char hdr[] = "This is just a header.\n";
   MPI_Status stat_mpi;
   MPI_Datatype subarray;
   MPI_File fh;
   MPI_Offset offset, end_of_hdr;
   MPI_Info info_mpi;

   MPI_Init(&argc, &argv);
   MPI_Comm_size(MPI_COMM_WORLD,&size_mpi);
   MPI_Comm_rank(MPI_COMM_WORLD,&rank_mpi);

   ttlcols = 2*COLS;
   /* Where are we in the array of processes? */
   col_mpi = rank_mpi%2;
   row_mpi = rank_mpi/2;
   /* Populate the array */
   for (j=0; j<ROWS; j++){
      for (i=0; i<COLS; i++){
         vals[j][i] = ttlcols*(ROWS*row_mpi + j) +
                      COLS*col_mpi + i;
      }
   } 
   /* MPI derived datatype for setting a file view */    
   starts[0] = row_mpi*ROWS;
   starts[1] = col_mpi*COLS;
   MPI_Type_create_subarray(2, sizes, subsizes, starts,
                            MPI_ORDER_C, MPI_INT,
                            &subarray); 
   MPI_Type_commit(&subarray);
   /* open the file */    
   printf("opening file\n");
   MPI_File_open(MPI_COMM_WORLD, "arrdata.dat", 
                 MPI_MODE_WRONLY | MPI_MODE_CREATE,
                 MPI_INFO_NULL, &fh);
   printf("opened file\n");
   /* set the initial file view */    
   MPI_File_set_view(fh, 0, MPI_CHAR, MPI_CHAR, "native", MPI_INFO_NULL);
   /* proc 0 writes first header */    
   if (rank_mpi == 0) {
      MPI_File_write(fh, (void*)hdr, strlen(hdr), MPI_CHAR, &stat_mpi);
      MPI_File_get_position(fh, &offset);
      MPI_File_get_byte_offset(fh, offset, &end_of_hdr); 
   }
   /* everybody has to know where proc 0 stopped writing */    
   MPI_Bcast((void*)&end_of_hdr, 1, MPI_INT, 0, MPI_COMM_WORLD);
   /* re-set file view for writing first array */    
   MPI_File_set_view(fh, end_of_hdr, MPI_INT,
                     subarray, "native",
                     MPI_INFO_NULL);
   /* and write the array */    
   MPI_File_write(fh, (void*)vals, ROWS*COLS, MPI_INT,
                  &stat_mpi);

   /* now go through the whole thing again to test */
   MPI_File_get_position(fh, &offset);
   MPI_File_get_byte_offset(fh, offset, &end_of_hdr); 
   MPI_File_set_view(fh, end_of_hdr, MPI_CHAR, MPI_CHAR, "native", MPI_INFO_NULL);
   if (rank_mpi == 0) {
      MPI_File_write(fh, (void*)hdr, strlen(hdr), MPI_CHAR, &stat_mpi);
      MPI_File_get_position(fh, &offset);
      MPI_File_get_byte_offset(fh, offset, &end_of_hdr); 
   }

   MPI_Bcast((void*)&end_of_hdr, 1, MPI_INT, 0, MPI_COMM_WORLD);

   MPI_File_set_view(fh, end_of_hdr, MPI_INT,
                     subarray, "native",
                     MPI_INFO_NULL);
   MPI_File_write(fh, (void*)vals, ROWS*COLS, MPI_INT,
                  &stat_mpi);
   MPI_File_close(&fh);

   MPI_Finalize();

   return 0;

}

【问题讨论】:

  • 您是否总是预先知道标题大小,它们是否都相同(大小)?如果是这样,您可以为包含标题和数据的进程#0 创建一个视图,并为仅包含数据的其他进程创建另一个视图。那么,对于排名 #0 的每次迭代,您只需调用一次 MPI_File_set_view() 和两次调用 MPI_File_write(),另一次调用一次。
  • @Gilles 我预先知道标题大小。不幸的是,它们并不完全相同。我无法改变这一点。不是我的设计。
  • 您的代码中有一个错误:end_of_hdr 的类型为 MPI_Offset,而您正在使用 MPI_INT 广播它。 MPI_Offset 通常是 64 位,而 MPI_INT 对应于 int,在 LP64 Unix 系统(*BSD、Linux、Solaris)上只有 32 位。在大端系统上,将广播一个非常错误的偏移量。使用 MPI_OFFSET(如果 MPI 实现支持)或相应的 C 类型(检查您的 mpi.h
  • @HristoIliev 谢谢。那时候我想过那个。在标准中找不到关于“MPI_OFFSET”的任何内容,所以我尝试了 MPI_INT。它起作用了,所以我想那一定是票。但我想不会。会调查的。再次感谢。
  • Strange、MPI_AINTMPI_OFFSET 至少从 2.2 版开始在标准的第 3.2.2 节(消息数据)中与所有其他预定义 MPI 数据类型一起描述。

标签: c io mpi mpi-io


【解决方案1】:

您的方法很好,如果您现在需要一些东西来将位放入文件中,请继续并称自己已完成。

以下是一些提高效率的建议:

  • 您可以查询状态对象以了解写入了多少字节,而不是获取位置并转换为字节。

  • 1234563然后所有进程都会发出一个集体调用。
  • 您应该使用集体 I/O 而不是独立 I/O。如果不是更好的性能,“质量库”应该能够为您提供同等的性能(如果没有,您可以提出 MPI 实现的问题)。

  • 如果进程有不同数量的数据要写入,MPI_EXSCAN 是收集谁拥有哪些数据的好方法。然后你可以调用 MPI_FILE_WRITE_AT_ALL 到文件中正确的偏移量。

【讨论】:

  • 谢谢!回复:你的第三个子弹,我只想要一个 proc 来写标题。我无法弄清楚如何在不隔离那些对 MPI_File_write 的调用的情况下做到这一点。有没有办法让每个人都调用 MPI_File_write,但只有一个 proc 进行实际写入?再次感谢!
  • 绝对。让 N 个进程调用一个集体操作是公平的,但其中只有一个有数据。任何进程都可以为“count”参数传入 0。但是,我建议使用单个集合,其中 0 级将标头与数组一起写入,而其他人则写入数组的一部分。
  • 明白了。再次感谢。
  • 您应该在写入数组时使用“MPI_File_write_all” - 与 MPI_File_write 相比,我已经看到大型数据集的性能提高了几个数量级。告诉 IO 库您正在进行集体写入(_all),使其能够在写入之前聚合来自不同进程的数据,例如导致少量的大 IO 事务而不是大量的小事务,这可以显着提高性能。
猜你喜欢
  • 2016-02-08
  • 1970-01-01
  • 2023-03-18
  • 2013-04-02
  • 2012-10-20
  • 2020-07-30
  • 1970-01-01
  • 2016-02-29
  • 2014-09-03
相关资源
最近更新 更多