c - 并发非重叠 pwrite() 到使用多个 MPI 进程安装在 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,j,k,N)*sizeof(double)+offset)!=8) perror("datasetparallel.dat");
//pwrite(fd,&value,sizeof(double),_numbering(i,j,k,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", O_RDWR | O_CREAT,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,&value,sizeof(double),_numbering(i,j,k,N)*sizeof(double)+offset)!=8) perror("datasetserial.dat");
//pwrite(fd,&value,sizeof(double),_numbering(i,j,k,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 将为您做到这一点。