【问题标题】:SIGBUS occurs when fortran code reads file on linux cluster当 fortran 代码读取 linux 集群上的文件时发生 SIGBUS
【发布时间】:2020-11-16 14:46:40
【问题描述】:

当我使用 mpirun 在 linux 集群上并行运行我的 fortran 代码时,我得到一个 sigbus 错误。 读取文件时出现,时间不规律,有时继续无误。 我已经尝试过调试编译选项,如 -g,但我没有得到任何关于错误来自哪一行的信息。 实际上,代码之前在三个不同的集群中执行过,没有出现这个错误,但这个错误只发生在这台机器上。 我个人怀疑这与机器的性能(尤其是存储 i/o)有关,但我不确定。

程序代码很简单。 mpirun 执行的每个进程都会读取其rank 对应的文件,如下所示。

!!!!!!!!!! start of code
OPEN(11, FILE='FILE_NAME_WITH_RANK', FORM='UNFORMATTED')
READ(11,*) ISIZE
ALLOCATE(SOME_VARIABLE(ISIZE))
DO I = 1, ISIZE
READ(11,*) SOME_VARIABLE(I)
ENDDO
READ(11,*) ISIZE2
ALLOCATE(SOME_VARIABLE2(ISIZE2))
DO I = 1, ISIZE2
READ(11,*) SOME_VARIABLE2(I)
ENDDO
! MORE VARIABLES
CLOSE(11)
!!!!!!!!!! end of code

我使用了 191 个 cpu,它加载的 191 个文件的总大小约为 11 GB。

用于执行的集群由 24 个节点组成,每个节点有 16 个 cpu(总共 384 个 cpu),并使用与另一个集群共享的公共存储。 我通过将节点 1 到 12 指定为主机文件来并行运行代码。

最初,我有 191 个 cpu 无序地同时读取所有文件。 这样做之后,程序以 sigbus 错误结束。此外,对于某些节点,ssh 连接被延迟,并且节点无法找到 bashrc 文件并出现 stale file handle 错误。 陈旧的文件句柄错误稍等片刻,它似乎自行恢复,但我不确定系统管理员做了什么。 所以,我把它改成了下面的代码,这样每次只有一个 cpu 可以读取文件。

!!!!!!!!!! start of code
DO ICPU = 0, NUMBER_OF_PROCESS-1
IF(ICPU.EQ.MY_PROCESS) CALL READ_FILE
CALL MPI_BARRIER(MPI_COMMUNICATOR,IERR)
ENDDO
!!!!!!!!!! end of code

这对于单次执行似乎工作正常,但如果我同时运行多个这些程序,第一个 mpirun 停止并最终以 sigbus 错误结束。

我的下一个尝试是通过在读取数组时删除 do 语句来最小化 read 语句的执行。但是,由于时间有限,我无法测试此修改的有效性。

这里有一些附加信息。

  • 如果我在运行并行程序时使用 nautilus 等资源管理器执行搜索或复制文件,则 nautilus 不响应或正在运行的程序引发 sigbus。在严重的情况下,我无法连接 VNC 服务器并出现过时文件句柄错误。
  • 我使用 OpenMPI 2.1.1、GNU Fortran 4.9.4。 我用以下方法编译程序 $OPENMPIHOME/bin/mpif90 -mcmodel=large -fmax-stack-var-size-64 -cpp -O3 $SOURCE -o $EXE 我在 gnome 终端中执行以下程序 $OPENMPIHOME/bin/mpirun -np $NP -x $LD_LIBRARY_PATH --hostfile $HOSTFILE $EXE
  • 据说该集群可以正常运行 FLUENT 等商业软件。

综上所述,我个人的怀疑是由于我的代码产生了过多的磁盘I/O,导致集群的存储被卸载,但我不知道这是否有意义,因为我没有集群知识。 如果是,我想知道是否有一种方法可以最小化磁盘 I/O,是否足以继续上面提到的矢量化 I/O,或者是否有额外的部分。 如果您能告诉我有关此问题的任何信息,我将不胜感激。 提前致谢。

!!! 我写了一个示例代码。如上所述,它可能不容易重现,因为出现次数因机器而异。

    PROGRAM BUSWRITE
    
    IMPLICIT NONE
    
    INTEGER, PARAMETER :: ISIZE1 = 10000, ISIZE2 = 20000, ISIZE3 = 30000
    DOUBLE PRECISION, ALLOCATABLE :: ARRAY1(:), ARRAY2(:), ARRAY3(:)
    INTEGER :: I
    INTEGER :: I1, I2, I3
    CHARACTER*3 CPUNUM
    
    INCLUDE 'mpif.h'
    INTEGER ISTATUS(MPI_STATUS_SIZE)
    INTEGER :: IERR, NPES, MYPE
    
    CALL MPI_INIT(IERR)
    CALL MPI_COMM_SIZE(MPI_COMM_WORLD,NPES,IERR)
    CALL MPI_COMM_RANK(MPI_COMM_WORLD,MYPE,IERR)
    
    I1=MOD(MYPE/100,10)+48
    I2=MOD(MYPE/10 ,10)+48
    I3=MOD(MYPE    ,10)+48
    CPUNUM=CHAR(I1)//CHAR(I2)//CHAR(I3)
    
    OPEN(11, FILE=CPUNUM//'.DAT', FORM='UNFORMATTED')
    ALLOCATE(ARRAY1(ISIZE1))
    ALLOCATE(ARRAY2(ISIZE2))
    ALLOCATE(ARRAY3(ISIZE3))
    DO I = 1, ISIZE1
    ARRAY1(I) = I
    WRITE(11) ARRAY1(I)
    ENDDO
    DO I = 1, ISIZE2
    ARRAY2(I) = I**2
    WRITE(11) ARRAY2(I)
    ENDDO
    DO I = 1, ISIZE3
    ARRAY3(I) = I**3
    WRITE(11) ARRAY3(I)
    ENDDO
    CLOSE(11)
    
    CALL MPI_FINALIZE(IERR)
    END PROGRAM
mpif90 -ffree-line-length-0 ./buswrite.f90 -o ./buswrite
mpirun -np 32 ./buswrite

我有 32 000.DAT ~ 031.DAT

    PROGRAM BUSREAD
    
    IMPLICIT NONE
    
    INTEGER, PARAMETER :: ISIZE1 = 10000, ISIZE2 = 20000, ISIZE3 = 30000
    DOUBLE PRECISION, ALLOCATABLE :: ARRAY1(:), ARRAY2(:), ARRAY3(:)
    INTEGER :: I
    INTEGER :: I1, I2, I3
    CHARACTER*3 CPUNUM
    
    INCLUDE 'mpif.h'
    INTEGER ISTATUS(MPI_STATUS_SIZE)
    INTEGER :: IERR, NPES, MYPE
    
    CALL MPI_INIT(IERR)
    CALL MPI_COMM_SIZE(MPI_COMM_WORLD,NPES,IERR)
    CALL MPI_COMM_RANK(MPI_COMM_WORLD,MYPE,IERR)
    
    I1=MOD(MYPE/100,10)+48
    I2=MOD(MYPE/10 ,10)+48
    I3=MOD(MYPE    ,10)+48
    CPUNUM=CHAR(I1)//CHAR(I2)//CHAR(I3)
    
    OPEN(11, FILE=CPUNUM//'.DAT', FORM='UNFORMATTED')
    ALLOCATE(ARRAY1(ISIZE1))
    ALLOCATE(ARRAY2(ISIZE2))
    ALLOCATE(ARRAY3(ISIZE3))
    DO I = 1, ISIZE1
    READ(11) ARRAY1(I)
    IF(ARRAY1(I).NE.I) STOP
    ENDDO
    DO I = 1, ISIZE2
    READ(11) ARRAY2(I)
    IF(ARRAY2(I).NE.I**2) STOP
    ENDDO
    DO I = 1, ISIZE3
    READ(11) ARRAY3(I)
    IF(ARRAY3(I).NE.I**3) STOP
    ENDDO
    CLOSE(11)
    
    CALL MPI_BARRIER(MPI_COMM_WORLD,IERR)
    IF(MYPE.EQ.0) WRITE(*,*) 'GOOD'
    CALL MPI_FINALIZE(IERR)
    END PROGRAM
mpif90 -ffree-line-length-0 ./busread.f90 -o ./busread
mpirun -np 32 ./busread

我已经按预期从终端获得了“GOOD”输出文本,但有问题的机器在运行 busread 时因 sigbus 错误而终止。

【问题讨论】:

  • 欢迎您,请在网站周围转tour。一个节点有多少个进程?一个节点中的所有进程是否都读取一个文件?如果担心我们真的需要更多代码和具体示例,请参阅 How to Askminimal reproducible example
  • 您是否在打开所有运行时检查选项的情况下运行您的程序(可能是 -fcheck=all ,但那是 gfortran 的古老版本,所以我不能 100% 确定)
  • 感谢 cmets。每个节点有 16 个线程,所以我每个节点使用 16 个进程。每个进程读取为进程号指定的每个文件,而不是节点号。我在文本中错误地写了节点号,但用进程号修复了它。很抱歉,我想附上实际的代码,但内容太庞大了,我试图只写相关部分作为示例。如上所述,此错误可能会或可能不会发生,具体取决于机器,因此我不确定它是否会有所帮助。另外,如果有什么我应该提供的,请告诉我。
  • 对于编译选项,我使用了以下选项。 -fbounds-检查 -fbacktrace 。我愿意用其他选项对其进行测试,但我认为由于设施访问问题而无法立即进行。
  • 无关注释:要将整数转换为3个字符的字符串,前导0,您可以写write(cpunum,'(i3.3)') mype

标签: fortran mpi gfortran openmpi sigbus


【解决方案1】:

设备重启后未观察到此问题。即使我在相同的条件下同时运行了 4 个程序,也没有出现问题。此外,其他使用该设备的团队也出现了类似问题,重启后得到解决。结论有点荒谬,但如果有遇到类似问题的人,我想总结如下。

如果您的程序在读取或写入文件时由于内存错误(如 sigbus 和 sigsegv)而异常终止,您可以检查以下内容。

  • 确保您的程序中没有错误。检查错误发生的时间是恒定的还是不规则的,其他程序是否有相同的症状,在其他机器上是否运行良好,用valgrind等内存错误检查工具运行时是否有问题。李>
  • 优化文件 I/O 部分。在 fortran 的情况下,处理整个数组比按元素处理快几十倍。
  • 出现错误后,立即尝试ssh连接机器(或节点),检查连接是否顺畅,文件系统是否访问良好。如果您无法访问bashrc文件或出现文件句柄陈旧等错误,请结合以上审核信息与系统管理员联系。

如果有人有什么要补充的,或者这篇文章不合适,请告诉我。

【讨论】:

    猜你喜欢
    • 2020-02-02
    • 2016-08-22
    • 2014-05-17
    • 2019-04-12
    • 1970-01-01
    • 1970-01-01
    • 2016-05-15
    • 2016-01-26
    • 1970-01-01
    相关资源
    最近更新 更多