微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用多个 MPI 进程并发非重叠 pwrite() 到挂载在 NFS 上的文件

如何解决使用多个 MPI 进程并发非重叠 pwrite() 到挂载在 NFS 上的文件

我有一个计算流体动态代码,我正在编写并行读写实现。我想要实现的是多个 MPI 进程打开同一个文件并向其写入数据(没有数据重叠,我使用带有偏移信息的 pwrite())。当两个 MPI 进程在同一个计算节点上时,这似乎工作正常。但是,当我使用 2 个或更多计算节点时,部分数据无法到达硬盘。为了证明这一点,我编写了以下使用 mpicc 编译的 C 程序(我的 MPI 发行版是 MPICH):

#include <mpi.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

long _numbering(long i,long j,long k,long N) {
  return (((i-1)*N+(j-1))*N+(k-1));
}

int main(int argc,char **argv)
{
  int   numranks,rank,fd,dd;
  long i,j,k,offset,N;
  double value=1.0;
  MPI_Init(NULL,NULL);
  MPI_Comm_size(MPI_COMM_WORLD,&numranks);
  MPI_Comm_rank(MPI_COMM_WORLD,&rank);
  N=10;
  offset=rank*N*N*N*sizeof(double);
  fd=-1;
  printf("opening file datasetparallel.dat\n");
  //while(fd==-1) {fd = open("datasetparallel.dat",O_RDWR | O_CREAT | O_SYNC,0666);}
  while(fd==-1) {fd = open("datasetparallel.dat",O_RDWR | O_CREAT,0666);}
  //while(dd==-1) {fd = open("/homeA/Desktop/",O_RDWR,0666);}

  for(i=1;i<=N;i++) {
    for(j=1;j<=N;j++) {
      for(k=1;k<=N;k++) {
        if(pwrite(fd,&value,sizeof(double),_numbering(i,N)*sizeof(double)+offset)!=8) perror("datasetparallel.dat");
        //pwrite(fd,N)*sizeof(double)+offset);
        value=value+1.0;
      }
    }
  }
  //if(close(fd)==-1) perror("datasetparallel.dat");
  fsync(fd); //fsync(dd);
  close(fd); //close(dd);
 
  printf("Done writing in parallel\n");
  if(rank==0) {
    printf("Beginning serial write\n");
    int ranknum;
    fd=-1;
    value=1.0;
    while(fd==-1) {fd = open("datasetserial.dat",0666);}
    for(ranknum=0;ranknum<numranks;ranknum++){
      offset=ranknum*N*N*N*sizeof(double); printf("Offset for rank %d is %ld\n",ranknum,offset);
      printf("writing for rank=%d\n",ranknum);
      for(i=1;i<=N;i++) {
        for(j=1;j<=N;j++) {
          for(k=1;k<=N;k++) {
            if(pwrite(fd,N)*sizeof(double)+offset)!=8) perror("datasetserial.dat");
            //pwrite(fd,N)*sizeof(double)+offset);
            value=value+1.0;
          }
        }
      }
      value=1.0;
    }
    //if(close(fd)==-1) perror("datasetserial.dat");
    fsync(fd);
    close(fd);
    printf("Done writing in serial\n");
  }
  MPI_Finalize();
  return 0;
}

上述程序以升序将双精度数写入文件。每个 MPI 进程写入相同的数字(1.0 到 1000.0),但写入文件的不同区域。例如,rank 0 将 1.0 写入 1000.0,而 rank 1 从 rank 0 写入 1000.0 之后的位置开始写入 1.0 至 1000.0。该程序输出一个名为 datasetparallel.dat 的文件,该文件已通过并发 pwrite()s 写入。它还输出 datasetserial.dat 以供参考以与 datasetparallel.dat 文件进行比较以检查其完整性(我通过在终端中使用 cmp 命令来执行此操作)。当使用 cmp 发现差异时,我使用 od 命令检查文件内容

od -N <byte_number> -tfD <file_name>

例如,我使用上述程序发现了一些丢失的数据(文件中的漏洞)。在并行写入的文件中,使用od命令的输出

.
.
.
0007660                      503                      504
0007700                      505                      506
0007720                      507                      508
0007740                      509                      510
0007760                      511                      512
0010000                        0                        0
*
0010620                        0
0010624

在串行编写的参考文件中,od 命令的输出

.
.
.
0007760                      511                      512
0010000                      513                      514
0010020                      515                      516
0010040                      517                      518
0010060                      519                      520
0010100                      521                      522
0010120                      523                      524
0010140                      525                      526
0010160                      527                      528
0010200                      529                      530
0010220                      531                      532
0010240                      533                      534
0010260                      535                      536
0010300                      537                      538
0010320                      539                      540
0010340                      541                      542
0010360                      543                      544
0010400                      545                      546
0010420                      547                      548
0010440                      549                      550
0010460                      551                      552
0010500                      553                      554
0010520                      555                      556
0010540                      557                      558
0010560                      559                      560
0010600                      561                      562
0010620                      563                      564
.
.
.

到目前为止,解决此问题的唯一方法似乎是使用带有 O_SYNC 标志的 POSIX open() 函数,以确保将文件物理写入硬盘驱动器,但这似乎慢得不切实际。另一种同样缓慢的方法似乎是使用内置的 MPI I/O 命令。我不确定为什么 MPI I/O 也很慢。存储已使用以下标志挂载在 NFS 上: rw,nohide,insecure,no_subtree_check,sync,no_wdelay 。我尝试在文件和目录上调用 fsync() 无济于事。因此,我需要有关如何解决此问题的建议。

解决方法

NFS 是一个可怕的文件系统。正如您所见,它的缓存行为使进程“错误地共享”缓存块然后破坏数据变得非常容易。

如果您坚持使用 NFS,请并行执行计算,然后从一个级别执行所有 I/O。

真正的并行系统,如 OrangeFS/PVFS (http://www.orangefs.org) 在这里会大有帮助,尤其是在开始使用 MPI-IO 时(您已经在使用 MPI,所以您已经成功了一半!)。光泽是另一种选择。 OrangeFS 是两者中配置更简单的一个,但我可能有偏见,因为我曾经在它上面工作过。

在集体 I/O 中寻址随机内存是绝对可能的。您的所有数据都是 MPI_DOUBLE,因此您需要做的就是描述最差 MPI_TYPE_CREATE_HINDEXED 的区域并提供地址。如果没有其他原因,您将看到性能的巨大提升,而不是发出一个 MPI-IO 调用而不是 (if N == 10) 1000。您的数据在文件中是连续的,因此您甚至不必担心关于文件视图。

此外,请记住我说过“从一个进程执行所有 I/O?”。这有点高级,但是如果您将“cb_nodes”提示(用于“集体缓冲”优化的节点数)设置为 1 MPI-IO,那么您就可以做到这一点。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。