在Fortran中使用OpenMP进行WH WHILE循环

时间:2018-01-23 05:51:56

标签: fortran openmp do-while

我在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

编译

1 个答案:

答案 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开放之前,代码在这里显着减慢了速度。