我在Fortran中创建了一个随机的点分布,这是通过do while循环完成的。我想通过OpenMP加速这个过程,但我读到你不能简单地使用!$ OMP PARALLEL DO for while while循环。我尝试将我原来的do转换为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
编译答案 0 :(得分:3)
我没有执行难以分发的while循环,而是提出以下建议:在数组索引上使用循环。
我想你想在数组zeta_list
中生成随机样本。我在平行循环中移动了一段时间。
但是,请注意您需要“支持OpenMP”的PRNG。在最近的gfortran版本中就是这种情况,我不知道其他编译器。
我还将1.0_16更改为a 1.0d0
,因为固定的数字常量不是一般指定种类参数并减小静态数组大小的好方法。
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开放之前,代码在这里显着减慢了速度。