首页 > 解决方案 > 为什么我使用 openMP atomic 的并行代码比串行代码花费更长的时间?

问题描述

我的序列代码片段如下所示。

 Program main
  use omp_lib
  Implicit None
   
  Integer :: i, my_id
  Real(8) :: t0, t1, t2, t3, a = 0.0d0

  !$ t0 = omp_get_wtime()
  Call CPU_time(t2)
  ! ------------------------------------------ !

    Do i = 1, 100000000
      a = a + Real(i)
    End Do

  ! ------------------------------------------ !
  Call CPU_time(t3)
  !$ t1 = omp_get_wtime()
  ! ------------------------------------------ !

  Write (*,*) "a = ", a
  Write (*,*) "The wall time is ", t1-t0, "s"
  Write (*,*) "The CPU time is ", t3-t2, "s"
End Program main

经过的时间: 在此处输入图像描述

通过使用 omp 指令doatomic,我将串行代码转换为并行代码。但是,并行程序比串行程序慢。我不明白为什么会这样。接下来是我的并行代码片段:

Program main
  use omp_lib
  Implicit None
    
  Integer, Parameter :: n_threads = 8
  Integer :: i, my_id
  Real(8) :: t0, t1, t2, t3, a = 0.0d0
 
  !$ t0 = omp_get_wtime()
  Call CPU_time(t2)
  ! ------------------------------------------ !

  !$OMP Parallel Num_threads(n_threads) shared(a)
  
   !$OMP Do 
     Do i = 1, 100000000
       !$OMP Atomic
       a = a + Real(i)
     End Do
   !$OMP End Do
  
  !$OMP End Parallel
  
  ! ------------------------------------------ !
  Call CPU_time(t3)
  !$ t1 = omp_get_wtime()
  ! ------------------------------------------ !

  Write (*,*) "a = ", a
  Write (*,*) "The wall time is ", t1-t0, "s"
  Write (*,*) "The CPU time is ", t3-t2, "s"
End Program main

经过的时间:在此处输入图像描述

所以我的问题是为什么我使用 openMP atomic 的并行代码比串行代码花费更长的时间?

标签: multithreadingperformanceparallel-processingfortranopenmp

解决方案


atomic在每次循环迭代中对同一个变量应用一个操作。此外,该变量在这些循环迭代之间具有相互依赖性。自然,与顺序版本相比,这会带来额外的开销(例如,同步、序列化成本和 CPU 周期)。此外,由于线程使其缓存无效,您可能会遇到很多缓存未命中。

此代码是应该使用reduction变量的典型代码a即, !$omp parallel do reduction(+:a))而不是原子操作。通过归约操作,每个线程将拥有变量的私有副本'a',并且在结束时parallel region,线程将减少他们的副本将变量'a'(使用'+'运算符)转换为单个值,该值将传播到'a'主线程的变量。

您可以在此SO 线程上找到有关 atomicreduction之间差异的更详细答案。在那个线程中,甚至还有一个代码,它(就像你的一样)它的版本比它的顺序版本慢几个数量级(慢 20 倍)。在那种情况下,它甚至比你的更糟糕(即 20x Vs 10x)。atomic


推荐阅读