You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Fortran+MPI并行计算结果随无关代码行变动问题排查

Fortran+MPI并行计算结果随无关代码行异常变动问题

问题现象

使用Fortran+mpi开展并行计算时,出现异常现象:增删代码中与核心计算逻辑完全无关的语句行时,计算结果会发生剧烈变化。
整套代码共分为三个部分:

  • 主程序IntColl.f90:包含代码的并行执行片段;
  • 模块m.f90:包含实现特定计算逻辑的大量函数,其中某函数内存在增删后会直接影响计算结果的代码行;
  • 模块Constants.f90:存储各类常量定义。
    代码总规模约2000行,以下仅展示与问题关联度最高的代码片段。

主程序并行代码片段

CN为进程总数,主程序中并行计算相关代码如下:

DO counter1 = 1, NG
        IF ( MOD ( counter1, CN ) .EQ. RANK ) THEN
            YDOT ( counter1      ) = FACTOR  * ABt  ( dmgw ( counter1 ) ) + &
                                     FACTOR1 * CIng ( dmgw ( counter1 ) )
            YDOT ( counter1 + NG ) = FACTOR  * ABMt ( dmgw ( counter1 ) )
        END IF
    END DO

    DO counter = 1, ( CN - 1 )
        IF ( RANK .EQ. counter ) THEN
            CALL MPI_SEND (       &
                YDOT ( counter ), &
                1,                &
                MPI_DP_VEC,       &
                0,                &
                1,                &
                MPI_COMM_WORLD,   &
                IERROR            &
                          )
        END IF
        IF ( RANK .EQ. 0 ) THEN
            CALL MPI_RECV (        &
                YDOT ( counter ),  &
                1,                 &
                MPI_DP_VEC,        &
                counter,           &
                1,                 &
                MPI_COMM_WORLD,    &
                MPI_STATUS_IGNORE, &
                IERROR             &
                          )
        END IF
    END DO

    CALL MPI_BCAST (          &
        YDOT      ( 1    ),   &
        2 * NG,               &
        MPI_DOUBLE_PRECISION, &
        RANK,                 &
        MPI_COMM_WORLD,       &
        IERROR                &
                   )

CIng函数代码片段

并行代码段调用的、来自m.f90模块的CIng ( p )函数代码如下:

REAL ( DP ) FUNCTION CIng ( p )     ! Collisional integral for \nu + \gamma
IMPLICIT NONE

    INTEGER :: i, CTR

    REAL ( DP ) :: p       ! p is coordinate momentum of initial neutrino: nu ( p ) + gamma ( k ) --> nu ( q ) + e^- ( q1 ) + e^+ ( q2 )
    REAL ( DP ) :: const_A
    REAL ( DP ) :: const_i
    REAL ( DP ) :: const
    REAL ( DP ) :: a, b

    REAL ( DP ) :: z1, z2, z3, z4, z5, z6, z7

    i   = 0
    CTR = 0

    const_A = 128.D+00 * PI * FSC * G_F ** 2 * ( M_E / X ) ** 2
    const_i = ( DSQRT ( YMAX ** 2 + X ** 2 ) - X ) ** 2 * YMAX / 2.D+00 ** 8 / PI ** 5 / p * ( M_E / X ) ** 3
    const = const_A * const_i

    a = 0.D+00

    z1 = 0.D+00
    z2 = 0.D+00
    z3 = 0.D+00
    z4 = 0.D+00
    z5 = 0.D+00
    z6 = 0.D+00
    z7 = 0.D+00

    DO i = 1, NS
        b = 0.D+00
        z1 = x1 ( i )
        z2 = x2 ( i )
        z3 = x3 ( i )
        z4 = x4 ( i )
        z5 = x5 ( i )
        z6 = x6 ( i )
        z7 = x7 ( i )
        b = UndInt ( p, z1, z2, z3, z4, z5, z6, z7 )
        a = a + b
        IF ( b .NE. 0.D+00 ) THEN
            CTR = CTR + 1
            WRITE ( 9, "(2ES23.15)" ) x2 ( i ), x3 ( i ) ! This #1
        END IF
    END DO

    print *, p, CTR, 'q'                                 ! This #2

    CIng = const * a / NS

END FUNCTION CIng

具体异常表现

当注释掉标注为This #1和This #2的两行I/O代码时,得到的计算结果与保留这两行时的结果存在灾难性差异;只要保留这两行中任意一行,得到的计算结果就符合预期的“正确结果”特征:

  • 正确结果(保留任意一行I/O代码、编译优化等级为-O3):输出为某物理量依赖关系,数据点落在分段光滑曲线上,符合理论预期的分段光滑特性;
  • 错误结果(注释掉两行I/O代码):输出点呈现无规律噪声特征,无明确依赖关系。

补充背景信息

  • 函数CIng ( p )会调用另一函数UndInt ( p, z1, z2, z3, z4, z5 ,z6, z7 ),后者会进一步调用其他下游函数,所有被调函数均仅实现数值计算逻辑,无特殊操作。
  • 数组x1 ( i )、x2 ( i )等在IntColl.f90中从文件读取数值完成初始化,这些数组声明于Constants.f90模块,主程序和m.f90模块均引用了该常量模块。
  • 计算结果与编译优化等级-O<n>相关:当This #1和This #2两行均被注释时,任意优化等级下得到的都是错误结果。
  • 编译时添加-Wall和-Wextra选项,输出大量两类警告:
    <variable> may be uninitialized in this function
    Inequality comparison for REAL(8) at (1)
    
    这些警告大部分来自程序中使用的第三方Fortran库函数(例如DLSODA)。
  • 编译时添加-fbacktrace -fcheck=all -g选项时,即使保留This #1和This #2中的任意一行,也会得到错误结果。
  • 当counter1从1遍历到NG(NG取值为96)时,CIng ( dmgw ( counter1 ) )的计算耗时单调递增。

运行环境

  • 代码标准:Fortran90
  • 编译命令:
    mpif90.mpich *.f90 *.f -O3 -o <程序名>
    
  • 运行命令:
    mpirun -np <进程数> ./<程序名>
    
  • 系统环境:Ubuntu 18.04.04 LTS,mpirun版本为3.3a2,编译器版本为GNU Fortran (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0。

问题根因与修复

最终排查确认该问题与多进程并行完全无关,根源是程序中存在执行路径上可能未初始化的变量。
m.f90模块中的UndInt_ ( ... )函数包含内部变量a和b,二者的赋值逻辑位于多层复杂嵌套IF条件分支内,若不在声明后立刻初始化,部分执行场景下这两个变量不会被赋值,会存储内存栈中的“脏数据”。
存在问题的函数代码如下,修复方式为声明变量后立刻将a、b初始化为0.0:

REAL ( DP ) FUNCTION UndInt_ ( p, k, eps1, eps2, cosTh1, cosTh2, phi1, phi2 )
IMPLICIT NONE

    REAL ( DP ), INTENT ( IN ) :: p, k, eps1, eps2, cosTh1, cosTh2, phi1, phi2

    REAL ( DP ) :: q1     ! q1 为电子坐标动量(电子动量乘以 X/M_E )
    REAL ( DP ) :: q2     ! q2 为正电子坐标动量
    REAL ( DP ) :: sinTh1
    REAL ( DP ) :: sinTh2
    REAL ( DP ) :: cosph1
    REAL ( DP ) :: cosph2

    REAL ( DP ) :: cosThk ! 电子极角余弦值
    REAL ( DP ) :: q      ! 出射中微子坐标动量绝对值

    REAL ( DP ) :: a, b

    a = 0.0 ! 修复点:声明后立刻初始化
    b = 0.0 ! 修复点:声明后立刻初始化

    q1     = DSQRT ( eps1 ** 2 - X ** 2 ) 
    q2     = DSQRT ( eps2 ** 2 - X ** 2 )
    sinTh1 = DSQRT ( 1.D+00 - cosTh1 ** 2 )
    sinTh2 = DSQRT ( 1.D+00 - cosTh2 ** 2 )
    cosph1 = DCOS  ( phi1 )
    cosph2 = DCOS  ( phi2 )

     IF ( ( k ** 2 * ( q1 * cosTh1          + q2 * cosTh2          - p ) ** 2      + &
            k ** 2 * ( q1 * sinTh1 * cosph1 + q2 * sinTh2 * cosph2     ) ** 2        &
          ) .LT.                                                                     &
          ( p * ( k - eps1 - eps2 ) - k * ( eps1 + eps2 ) + eps1 * eps2 + X ** 2   + &
            p * q1 * cosTh1 + p * q2 * cosTh2                                      - &
            q1 * q2 * ( sinTh1 * sinTh2 * DCOS ( phi1 - phi2 ) + cosTh1 * cosTh2 )   &
          ) ** 2                                                                     &
        ) THEN
         a = 0.0
         b = 0.0
     ELSE
         IF (   ( cosThk1 ( p, k, eps1, eps2, cosTh1, cosTh2, phi1, phi2 ) .GT.  1.D+00 ) &
           .OR. ( cosThk1 ( p, k, eps1, eps2, cosTh1, cosTh2, phi1, phi2 ) .LT. -1.D+00 ) &
          .AND. ( cosThk2 ( p, k, eps1, eps2, cosTh1, cosTh2, phi1, phi2 ) .GT.  1.D+00 ) &
           .OR. ( cosThk2 ( p, k, eps1, eps2, cosTh1, cosTh2, phi1, phi2 ) .LT. -1.D+00 ) &
            ) THEN
             a = 0.0
             b = 0.0
         ELSE
             IF ( ( DABS ( p + k - eps1 - eps2                                          - &
                     q_1 ( p, k, eps1, eps2, cosTh1, cosTh2, phi1, phi2 )                 &
                         )                                                              / &
                         ( p + k - eps1 - eps2 )                                          &
                  ) .LT. 1.D-015                                                          &
                ) THEN
                cosThk = cosThk1 ( p, k,    eps1, eps2,         cosTh1, cosTh2, phi1, phi2 )
                q      =     q_1 ( p, k,    eps1, eps2,         cosTh1, cosTh2, phi1, phi2 )
                a      = UndInt1 ( p, k, q, eps1, eps2, cosThk, cosTh1, cosTh2, phi1, phi2 )
             END IF
             IF ( ( DABS ( p + k - eps1 - eps2                                          - & 
                     q_2 ( p, k, eps1, eps2, cosTh1, cosTh2, phi1, phi2 )                 &
                         )                                                              / &
                         ( p + k - eps1 - eps2 )                                          &
                  ) .LT. 1.D-015 .AND.                                                    &
                  ( DABS (                                                                &
               ( cosThk1 ( p, k, eps1, eps2, cosTh1, cosTh2, phi1, phi2 )               - &
                 cosThk2 ( p, k, eps1, eps2, cosTh1, cosTh2, phi1, phi2 ) )             / &
                 cosThk1 ( p, k, eps1, eps2, cosTh1, cosTh2, phi1, phi2 )                 &
                         )                                                                &
                  ) .GT. 1.D-015                                                           &
                ) THEN
                cosThk = cosThk2 ( p, k,    eps1, eps2,         cosTh1, cosTh2, phi1, phi2 )
                q      =     q_2 ( p, k,    eps1, eps2,         cosTh1, cosTh2, phi1, phi2 )
                b      = UndInt1 ( p, k, q, eps1, eps2, cosThk, cosTh1, cosTh2, phi1, phi2 )
            END IF
        END IF
    END IF

    UndInt_ = a + b

END FUNCTION UndInt_

原因说明

函数UndInt_ ( ... )的返回值由a和b相加得到,未初始化时变量中的垃圾值会直接传入返回值,最终导致计算结果异常。增删无关I/O语句会改变栈内存布局,恰好覆盖或暴露了脏数据问题,才出现了结果随无关代码行变动的现象。
编译时开启-fbacktrace -fcheck=all -g运行时检查选项可快速定位此类bug,编码过程中需严格遵守变量初始化规范,不要忽略编译器给出的未初始化变量警告。


内容的提问来源于stack exchange,提问作者Vlad Yurchenko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 21:51:19