【问题标题】:DO WHILE loop with OpenMP in Fortran在 Fortran 中使用 OpenMP 进行 DO WHILE 循环
【发布时间】:2018-01-23 05:51:55
【问题描述】:

我正在 Fortran 中创建点的随机分布,这是通过 do while 循环完成的。我想通过 OpenMP 加速这个过程,但我读到你不能简单地将 !$OMP PARALLEL DO 用于 do while 循环。我尝试将我原来的 do while 转换为嵌套在 do while 中的 do 循环。但是,我在代码中看不到任何加速,我的意思是它需要与串行版本相同的时间。我似乎无法弄清楚问题是什么,并且我被卡住了,不胜感激。我已经展示了下面的代码。

原来的循环:

!OMP PARALLEL DO
do while (count < size(zeta_list,2))
    call random_number(x)
    call random_number(y)
    x1 = a + FLOOR((b+1-a)*x)
    y1 = a + FLOOR((b+1-a)*y)
    if (abs(y1) <= abs(1/x1)) then
        count = count + 1
        call random_number(theta)
        zeta_list(1,count) = x1*sin(2*pi_16*theta)
        zeta_list(2,count) = x1*cos(2*pi_16*theta)
    end if 
end do  
!OMP END PARALLEL DO

在我尝试转换后,

!$OMP PARALLEL 
do while (count < size(zeta_list,2))
    !$OMP DO
    do i=1,size(zeta_list,2),1
        call random_number(x)
        call random_number(y)
        x1 = a + FLOOR((b+1-a)*x)
        y1 = a + FLOOR((b+1-a)*y)
        if (abs(y1) <= abs(1/x1)) then
            call random_number(theta)
            count = count + 1
            zeta_list(1,i) = x1*sin(2*pi_16*theta)
            zeta_list(2,i) = x1*cos(2*pi_16*theta)
        end if
    end do
    !$OMP END DO 
end do  
!$OMP END PARALLEL

整个代码是

PROGRAM RANDOM_DISTRIBUTION

IMPLICIT NONE 

DOUBLE PRECISION, DIMENSION(2,1000000)::zeta_list
DOUBLE PRECISION::x,y,x1,y1,theta
REAL::a,b,n
INTEGER::count,t1,t2,clock_rate,clock_max,i
DOUBLE PRECISION,PARAMETER::pi_16=4*atan(1.0_16)

call system_clock ( t1, clock_rate, clock_max )

n = 1000
b = n/2
a = -n/2
count = 0
zeta_list = 0
x = 0
y = 0
x1 = 0 
y1 = 0 
theta = 0

call random_seed()



!$OMP PARALLEL 
do while (count < size(zeta_list,2))
    !$OMP DO
    do i=1,size(zeta_list,2),1
        call random_number(x)
        call random_number(y)
        x1 = a + FLOOR((b+1-a)*x)
        y1 = a + FLOOR((b+1-a)*y)
        if (abs(y1) <= abs(1/x1)) then
            call random_number(theta)
            count = count + 1
            zeta_list(1,i) = x1*sin(2*pi_16*theta)
            zeta_list(2,i) = x1*cos(2*pi_16*theta)
        end if
    end do
    !$OMP END DO 
end do  
!$OMP END PARALLEL


call system_clock ( t2, clock_rate, clock_max )
write ( *, * ) 'Elapsed real time = ', real ( t2 - t1 ) / real ( clock_rate) ,'seconds' 


stop
END PROGRAM RANDOM_DISTRIBUTION

使用 gfortran test.f90 -fopenmp 编译

【问题讨论】:

  • 你如何衡量加速比?展示实际结果。代码中未显示的部分有多大(se minimal reproducible example)?你的变量是如何声明的?
  • 您使用哪些编译器?并非所有编译器都允许 random_number 并行,有些编译器可能会串行生成数字,即使它们允许 random_number 并行。有可用的并行随机数生成器。
  • 关于您的样本有很多话要说。 1. 结果将取决于zeta_list 在其第二维中的大小。如果它很小,线程的成本将使任何加速都不太可能。 2. 在不声明“线程绑定”变量的情况下使用 OpenMP 会使结果错误并且代码变慢。 3. 正如 Vladimir 所提到的,并行随机数不是显而易见的事情。 4. pi 变量上有一个看起来很无辜的_16 下划线。你的“打字”计划是什么?
  • 这还没有答案,但简而言之:用一个更简单的例子启动 OpenMP,使用测试来检查 OpenMP 后的结果是否有效,并行记录随机数功能。这 依赖于编译器,因此您可能希望为您的代码选择一个固定的编译器或使用提供线程安全随机数生成的库。
  • 我明白了,我应该检查一下线程安全的 rngs。我使用挂钟时间测量加速,其余代码非常小,让我重新编辑我的初始帖子以显示整个内容。我一直在使用 gfortran 编译,但我也有 ifort(没试过)。我尝试使用较大的 zeta_list 大小来掩盖线程成本,并且我之前使用过 openmp,只是对 do while 循环及其变量声明的逻辑感到困惑。

标签: fortran openmp do-while


【解决方案1】:

我建议使用以下方法,而不是执行难以分发的 while 循环:在数组索引上使用循环。

我假设您想在数组zeta_list 中生成随机样本。我在并行循环中移动了 while。

不过,请注意您需要“支持 OpenMP”的 PRNG。在最近的 gfortran 版本中就是这种情况,其他编译器我不知道。

我还将 1.0_16 更改为 1.0d0,因为固定数字常量通常不是指定 kind 参数的好方法,并且减小了静态数组的大小。

PROGRAM RANDOM_DISTRIBUTION

  IMPLICIT NONE 

  DOUBLE PRECISION, DIMENSION(2,100000)::zeta_list
  DOUBLE PRECISION::x,y,x1,y1,theta
  REAL::a,b,n
  INTEGER::count,t1,t2,clock_rate,clock_max,i
  DOUBLE PRECISION,PARAMETER::pi_16=4*atan(1.0d0)

  call system_clock ( t1, clock_rate, clock_max )

  n = 1000
  b = n/2
  a = -n/2
  count = 0
  zeta_list = 0
  x = 0
  y = 0
  x1 = 0 
  y1 = 0 
  theta = 0

  call random_seed()

  !$OMP PARALLEL DO private(i, x, y, x1, y1, theta)
  do i = 1, size(zeta_list, 2)
     inner_loop: do
        call random_number(x)
        call random_number(y)
        x1 = a + FLOOR((b+1-a)*x)
        y1 = a + FLOOR((b+1-a)*y)
        if (abs(y1) <= abs(1/x1)) then
           call random_number(theta)
           zeta_list(1,i) = x1*sin(2*pi_16*theta)
           zeta_list(2,i) = x1*cos(2*pi_16*theta)
           exit inner_loop
        end if
     end do inner_loop
  end do
  !$OMP END PARALLEL DO

  write(*,*) zeta_list(:,1)
  write(*,*) zeta_list(:,2)

  call system_clock ( t2, clock_rate, clock_max )
  write ( *, * ) 'Elapsed real time = ', real ( t2 - t1 ) / real ( clock_rate) ,'seconds' 

END PROGRAM RANDOM_DISTRIBUTION

在 OpenMP 线程中使用 random_number 对于 gfortran 5 是安全的,但您需要 gfortran 7 才能获得 线程 随机数生成器。我列出了两个核心的时序:

user@pc$ gfortran-5 -O3 -Wall -fopenmp -o prd prd.f90
user@pc$ OMP_NUM_THREADS=1 ./prd
   47.496326386583306        237.29327630545950     
  -101.11803913888293        147.70288474064185     
 Elapsed real time =    3.47700000     seconds
user@pc$ OMP_NUM_THREADS=2 ./prd
   0.0000000000000000       -0.0000000000000000     
  -160.53394672041205        49.526275353269853     
 Elapsed real time =    12.1479998     seconds
user@pc$ rm fort.1*
user@pc$ gfortran-5 -O3 -Wall -fopenmp -o prd prd.f90
user@pc$ OMP_NUM_THREADS=1 ./prd
 Elapsed real time =    3.05100012     seconds
user@pc$ OMP_NUM_THREADS=2 ./prd
 Elapsed real time =    9.09599972     seconds
user@pc$ gfortran-6 -O3 -Wall -fopenmp -o prd prd.f90
user@pc$ OMP_NUM_THREADS=1 ./prd
 Elapsed real time =    3.09200001     seconds
user@pc$ OMP_NUM_THREADS=2 ./prd
 Elapsed real time =    12.3350000     seconds
user@pc$ gfortran-7 -O3 -Wall -fopenmp -o prd prd.f90
user@pc$ OMP_NUM_THREADS=1 ./prd
 Elapsed real time =    1.83200002     seconds
user@pc$ OMP_NUM_THREADS=2 ./prd
 Elapsed real time =   0.986999989     seconds

结果非常明显:在 gfortran 7 OpenMP-ing 之前,此处的代码会显着降低速度。

【讨论】:

  • @francescalus 是的
  • 我尝试了一个带有 (2,1e6) 的 zeta_list,有 4 个 omp 线程,与串行相比,似乎要慢得多。串行:24.9 秒,4 线程:115.7 秒。我也尝试了 1e7 以确保开销不是问题。好像不是。我的 Gfortran 版本是 5.4。这个版本是不是太旧了?
  • @ThunderFlash 我不知道您是否收到编辑通知。我添加了时间,你需要 gfortran >= 7
  • @PierredeBuyl 是的,您不会收到编辑通知。啊,那让我检查一下,谢谢!
  • 是否需要类似“USE OMP”?
猜你喜欢
  • 1970-01-01
  • 2022-11-29
  • 1970-01-01
  • 1970-01-01
  • 2021-07-18
  • 2012-08-26
  • 2016-08-13
  • 2016-09-18
  • 2016-03-27
相关资源
最近更新 更多