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

OpenMP并行化嵌套循环中k维度距离计算的实现咨询

Fortran子程序并行化:k值维度的距离计算优化

问题描述

现有如下Fortran子程序,希望并行化针对每个k值的距离计算过程(即每个线程处理一个k值,例如k=3时,线程1处理k=1,线程2处理k=2,线程3处理k=3)。尝试过omp_nested、omp_ordered指令但出现错误,需要可行的并行化方案。

原代码

subroutine min_distance(r,n,k,centroid,distance,indices,distancereg)
        integer, intent(out):: n,k
        real,dimension(:,:),intent(in),allocatable::centroid
        real,dimension(:,:),intent(in),allocatable::r
        integer,dimension(:),intent(out),allocatable::indices,distancereg
        real ::d_min
        integer::y,i_min,j,i
        integer,parameter :: data_dim=2
        allocate (indices(n))
        allocate (distancereg(k))
        !cost=0.d0
        
        do j=1,n

           i_min = -1
           d_min=1.d6
         
           do i=1,k
              distance=0.d0
              distancereg(i)=0.d0
              do y=1,data_dim
                 distance = distance+abs(r(y,j)-centroid(y,i))
                 distancereg(i)=distancereg(i)+abs(r(y,j)-centroid(y,i))
              end do
          
              if (distance < d_min) then
                 d_min=distance
                 i_min=i
              end if
           end do
           if( i_min < 0 ) print*," found error by assigning k-index to particle ",j
           indices(j)=i_min
        end do
end subroutine min_distance

并行化分析与解决方案

原代码核心问题

  1. 共享变量竞争:distance是单个标量,多线程同时写入会导致数据混乱;d_min和i_min是每个j循环内的全局最小值与对应索引,直接并行内层循环会引发多线程同时修改的竞争,结果不可控。
  2. 不必要的指令误用:你的场景不需要嵌套并行(omp_nested)或有序执行(omp_ordered),这些指令反而会增加调度复杂度,引发错误。

可行的OpenMP并行方案

方案1:线程私有变量+单次临界区合并(高效推荐)

每个线程先计算自己负责的k范围内的局部最小值,最后仅通过一次临界区合并全局结果,避免频繁同步开销:

subroutine min_distance(r,n,k,centroid,distance,indices,distancereg)
        integer, intent(out):: n,k
        real,dimension(:,:),intent(in),allocatable::centroid
        real,dimension(:,:),intent(in),allocatable::r
        integer,dimension(:),intent(out),allocatable::indices,distancereg
        real ::d_min
        integer::y,i_min,j,i
        integer,parameter :: data_dim=2
        ! 线程私有变量:存储每个线程的局部最小值与对应索引
        real :: local_d_min
        integer :: local_i_min
        allocate (indices(n))
        allocate (distancereg(k))
        !cost=0.d0
        
        do j=1,n
           i_min = -1
           d_min=1.d6
         
           !$OMP PARALLEL PRIVATE(i,y,distance,local_d_min,local_i_min) SHARED(j,r,centroid,d_min,i_min,distancereg)
           ! 初始化线程局部最小值
           local_d_min = 1.d6
           local_i_min = -1

           !$OMP DO
           do i=1,k
              distance=0.d0
              distancereg(i)=0.d0
              do y=1,data_dim
                 distance = distance+abs(r(y,j)-centroid(y,i))
                 distancereg(i)=distancereg(i)+abs(r(y,j)-centroid(y,i))
              end do
          
              ! 仅更新线程局部的最小值与索引
              if (distance < local_d_min) then
                 local_d_min=distance
                 local_i_min=i
              end if
           end do
           !$OMP END DO

           ! 临界区:合并线程局部结果到全局变量
           !$OMP CRITICAL
           if (local_d_min < d_min) then
              d_min = local_d_min
              i_min = local_i_min
           end if
           !$OMP END CRITICAL

           !$OMP END PARALLEL

           if( i_min < 0 ) print*," found error by assigning k-index to particle ",j
           indices(j)=i_min
        end do
end subroutine min_distance

方案2:并行循环+临界区实时更新最小值

适合k值较小的场景,每次计算完单个k的距离后,通过临界区更新全局最小值:

subroutine min_distance(r,n,k,centroid,distance,indices,distancereg)
        integer, intent(out):: n,k
        real,dimension(:,:),intent(in),allocatable::centroid
        real,dimension(:,:),intent(in),allocatable::r
        integer,dimension(:),intent(out),allocatable::indices,distancereg
        real ::d_min
        integer::y,i_min,j,i
        integer,parameter :: data_dim=2
        allocate (indices(n))
        allocate (distancereg(k))
        !cost=0.d0
        
        do j=1,n
           i_min = -1
           d_min=1.d6
         
           !$OMP PARALLEL DO PRIVATE(i,y,distance) SHARED(j,r,centroid,d_min,i_min,distancereg)
           do i=1,k
              distance=0.d0
              distancereg(i)=0.d0
              do y=1,data_dim
                 distance = distance+abs(r(y,j)-centroid(y,i))
                 distancereg(i)=distancereg(i)+abs(r(y,j)-centroid(y,i))
              end do
          
              ! 临界区:确保全局变量仅被单个线程修改
              !$OMP CRITICAL
              if (distance < d_min) then
                 d_min=distance
                 i_min=i
              end if
              !$OMP END CRITICAL
           end do
           !$OMP END PARALLEL DO

           if( i_min < 0 ) print*," found error by assigning k-index to particle ",j
           indices(j)=i_min
        end do
end subroutine min_distance

关键注意事项

  • 变量作用域:必须将i、y、distance设为私有(PRIVATE),避免线程间互相干扰;j、r、centroid是每个j循环的输入,设为共享(SHARED)。
  • distancereg的线程安全:每个线程处理不同的i,写入distancereg(i)时不存在竞争,无需额外同步。
  • 编译要求:编译时需添加OpenMP编译选项,例如GCC的-fopenmp、Intel Fortran的-qopenmp。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 17:10:23