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

OpenMP多线程(>1)运行结果异常,寻求排查建议

前言

我了解有许多类似标题的问题,已查阅大部分,但本次问题成因可能不同,故发帖求助,期待您的判断与建议。

重要说明:我本想提供最小复现示例,但涉及代码规模大且包含不可共享的依赖,故将尽可能详细说明代码结构,并提供一个无错误的示例以展示整体结构。

核心说明

初始代码

我尝试并行化的核心代码是多层嵌套循环,仅显式并行化外层1到4的循环:

!$omp parallel do &
!$omp   default(firstprivate), &
!$omp   private(idir, timer, zone_title), &
!$omp   shared(DIRS_CH, ROTATIONS, NLimsP1, COORDS_DIR_CH, LIM_SIGN_DIRS &
!$omp          , policies, limits, df_I_ref, df_J_ref, msh_ZoneLimsInterestModes &
!$omp          , bases_ch, refmts, deltas, basePts, base_i &
!$omp          , struct_data, wd, settings, logger_debug &
!$omp          , id_im_last, maxF, NLims, getBFM_msh &
!$omp          , NFREQS, NNODES, NNODESL, NLIBS, NLIBSL &
!$omp          , NMODES, NMODES_EFF, MODES &
!$omp          , NPSDEL, NTCOMPS, NDIRS, TCOMPS, DIRS &
!$omp          , msh_izone, msh_totNPts, m3mf_msh_ptr_), &
!$omp   num_threads(4)
do idir = 1, N_DIRS
   ! step 1: general setup
   ...
   do ilim = 1, NLims
      ! step 2: other (more specific) setup
      ...
      ! HERE: main computation
      call rz%compute()
   enddo
   ! step 3: non relevant stuff..
   ...
enddo ! n dirs
!$omp end parallel do

核心计算(类型绑定)过程

如前一段代码注释所示,主计算在OMP threadprivate的派生类型变量rz的compute()过程中(注:示例中用proc_2()过程模拟):

module subroutine compute(this)
   ...
   use omp_lib
   implicit none
   ...
   real(RDP), allocatable :: rres(:, :), intg(:)
   ...

   ! some computation based on *this instance's state
   ...

   allocate(intg(...))

   ! now invoking the main computing function pointer
   rres(:, 1) = getBFM_msh(...)
   
   ! for each of these calls, the local integral gets updated
   intg(:) = rres(:, 1) * somevar ! at first usage only assignment!!
   ...
   intg(:) = intg(:) + rres(:, 1) * somevar ! then, increment

   ! NOTE: this is done many times, even in some do loops !
   ...
   ...

   ! Once local integral is computed, global is updated and data dumped onto a global unit
   ! NOTE: critical to avoid data races and to guard file access to one thread only.
   !$omp critical
   m3mf_msh_ptr_ = m3mf_msh_ptr_ + intg   ! update main integral (shared OMP variable)

   call dumpData(this, rres)
   !$omp end critical
end subroutine

核心计算函数指针

实际核心计算在getBFM_msh()函数中,该函数接收输入并返回计算结果,后续会通过积分得到最终结果。函数内包含多层嵌套循环,最关键的是调用了LAPACK的dgesvd()例程:

module function procPtr_(...) result(rres)
   ...
   
   ! internal data allocation
   ...
   
   do itc = 1, NTCOMPS   ! first loop

      ...
      call dgesvd(&
              'O' &           ! min(M,N) columns of U are overwritten on array A (saves memory)
            , 'N' &           ! no rows of V are computed
            , NNODESL    &    ! n. of rows M
            , NNODESL    &    ! n. of cols N
            , S_uvw_w1   &    ! A matrix (overwritten with left-singular vectors)
            , NNODESL    &
            , D_S_uvw_w1 &    ! singular values
            , tmpv       &    ! U
            , 1          & 
            , tmpv       &    ! VT
            , 1          &
            , MSHR_SVD_WORK  &
            , MSHR_SVD_LWORK &
            , MSHR_SVD_INFO  &
         )

      ! Some other computations, which lead to  "rres"
      ...
   enddo   ! itc
end function

其中tmpv、S_uvw_w1和D_S_uvw_w1为局部变量,MSHR_SVD_WORK、MSHR_SVD_LWORK和MSHR_SVD_INFO为全局模块变量,已设为firstprivate(因每次调用dgesvd()都会修改这些变量)。

主要问题

单线程运行时所有功能正常,但线程数设置为>1时结果错误。调试发现,调用dgesvd()后,作为输入输出的变量S_uvw_w1在单线程和多线程下存在差异,因此怀疑LAPACK例程自身已并行,导致并行嵌套出现问题。除此之外,我无法确定其他可能的错误原因。

示例代码
module mod1

   implicit none
   integer :: sum_ = 0, ival_ = 0

   procedure(interf), pointer :: procPtr_ => null()
   abstract interface
      function interf(ival) result(ires)
         integer, intent(in) :: ival
         integer :: ires
      end function
   end interface

contains

   function ifunc1(ival) result(ires)
      integer, intent(in) :: ival
      integer :: ires

      if (mod(ival, 2) == 0) then
         ires = ival * 2
      else
         ires = ival
      endif
   end function

end module


program main

   use omp_lib
   use mod1
   implicit none

   procPtr_ => ifunc1
   call proc_1()
   print *, ' total sum = ', sum_

contains

   subroutine proc_1()
      integer :: i, j

      ! from some previous computations..
      sum_ = 10

      !$omp parallel do default(firstprivate) shared(sum_) &
      !$omp   num_threads(4)
      do i = 1, 4
         do j = 1, 10
            ival_ = i * j
            call proc_2()
         enddo
      enddo
      !$omp end parallel do
   end subroutine

   subroutine proc_2()
      integer :: j, temp_

      temp_ = procPtr_(ival_)

      !$omp critical
      sum_ = sum_ + temp_
      !$omp end critical
   end subroutine

end program

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 05:05:44