Fortran OMP:如何做并行和单一任务?

Fortran OMP : how to do a parallel and a single task?

我是并行编程的新手。这是我想要并行化的串行代码

program main
  implicit none
  integer :: pr_number, i, pr_sum
  real :: pr_av
  
  pr_sum = 0
  do i=1,1000
! The following instruction is an example to simplify the problem.
! In the real case, it takes a long time that is more or less the same for all threads
! and it returns a large array
   pr_number = int(rand()*10) 
   pr_sum = pr_sum+pr_number
   pr_av = (1.d0*pr_sum) / i
   print *,i,pr_av ! In real case, writing a huge amount of data on one file
 enddo

 end program main

我想并行化 pr_number = int(rand()*10) 并且每个 num_threads 只有一个 print。 我尝试了很多东西,但没有用。例如,

program main
  implicit none
  integer :: pr_number, i, pr_sum
  real :: pr_av
  
  pr_sum = 0
!$OMP PARALLEL DEFAULT(SHARED) PRIVATE(pr_number) SHARED(pr_sum,pr_av)
!$OMP DO REDUCTION(+:pr_sum)
  do i=1,1000
   pr_number = int(rand()*10)
   pr_sum = pr_sum+pr_number
!$OMP SINGLE
   pr_av = (1.d0*pr_sum) / i
   print *,i,pr_av
!$OMP END SINGLE
 enddo
!$OMP END DO
!$OMP END PARALLEL

end program main

我在编译时收到一条错误消息:工作共享区域可能没有紧密嵌套在工作共享、关键或显式任务区域中。

我怎样才能得到这样的输出(例如,如果我有 4 个线程)?

       4   3.00000000    
       8   3.12500000    
      12   4.00000000    
      16   3.81250000    
      20   3.50000000  
      ...

我再说一遍,我是并行编程的初学者。我在 Whosebug 上阅读了很多东西,但我认为,我还没有理解的技能。我正在努力,但是...

编辑 1

按照评论中的建议进行解释。 A do loop 执行 N 次冗长的计算(N 次马尔可夫链蒙特卡洛),并且在每次迭代时将所有计算的平均值写入文件。之前的平均值被删除,只保留最后一个,所以可以继续处理。我想在 4 个线程上并行执行此计算。 这是我想做的,但也许这不是最好的主意。

感谢您的帮助。

在发生缩减的结构中,缩减变量的值没有很好地定义。带有求和的归约子句通常由每个线程实现,每个线程都有一个归约变量的私有副本,它们用于对那个线程的数字求和。在循环的 and 处,私有副本被加到最终总和中。在实际进行减少之前打印中间值没有什么意义。

您可以在嵌套循环中进行缩减,并每 n 次迭代打印一次中间结果

program main
  implicit none
  integer :: pr_number, i, j, pr_sum
  real :: pr_av
  
  pr_sum = 0
  !$OMP PARALLEL DEFAULT(SHARED) PRIVATE(pr_number) SHARED(pr_sum,pr_av)
  do j = 1, 10
    !$OMP DO REDUCTION(+:pr_sum)
      do i=1,100
      pr_number = int(rand()*10)
      pr_sum = pr_sum+pr_number
    enddo
    !$OMP END DO

    !$omp single
    pr_av = (1.d0*pr_sum) / 100
    print *,j*100,pr_av
    !$omp end single
  end do
  !$OMP END PARALLEL

end program main

我保留了相同的 rand(),它可能会或可能不会正确并行工作,具体取决于编译器。即使它给出了正确的结果,它实际上可能是使用一些锁或屏障顺序执行的。但是,要点也适用于其他库。

结果

> gfortran -fopenmp reduction-intermediate.f90 
> ./a.out 
     100   4.69000006    
     200   9.03999996    
     300   13.7600002    
     400   18.2299995    
     500   22.3199997    
     600   26.5900002    
     700   31.0599995    
     800   35.4300003    
     900   40.1599998