Arrays Fortran中大型阵列的OpenMP简化
我知道有时会有人问类似的问题:,甚至在英特尔论坛()中也是如此,但我想知道你的看法,因为我得到的可扩展性不是我所期望的 所以我需要填充一个非常大的复数数组,我想用OpenMP并行化。我们的第一种方法是:Arrays Fortran中大型阵列的OpenMP简化,arrays,fortran,openmp,fortran90,reduction,Arrays,Fortran,Openmp,Fortran90,Reduction,我知道有时会有人问类似的问题:,甚至在英特尔论坛()中也是如此,但我想知道你的看法,因为我得到的可扩展性不是我所期望的 所以我需要填充一个非常大的复数数组,我想用OpenMP并行化。我们的第一种方法是: COMPLEX(KIND=DBL), ALLOCATABLE :: huge_array(:) COMPLEX(KIND=DBL), ALLOCATABLE :: thread_huge_array(:) INTEGER :: huge_number, index1, index2, index
COMPLEX(KIND=DBL), ALLOCATABLE :: huge_array(:)
COMPLEX(KIND=DBL), ALLOCATABLE :: thread_huge_array(:)
INTEGER :: huge_number, index1, index2, index3, index4, index5, bignumber1, bignumber2, smallnumber, depending_index
ALLOCATE(huge_array(huge_number))
!$OMP PARALLEL FIRSTPRIVATE(thread_huge_array)
ALLOCATE(thread_huge_array(SIZE(huge_array)))
thread_huge_array = ZERO
!$OMP DO
DO index1=1,bignumber1
! Some calculations
DO index2=1,bignumber2
! Some calculations
DO index3=1,6
DO index4=1,6
DO index5=1,smallnumber
depending_index = function(index1, index2, index3, index4, index5)
thread_huge_array(depending_index) = thread_huge_array(depending_index)
ENDDO
ENDDO
ENDDO
ENDDO
ENDDO
!$OMP END DO
!$OMP BARRIER
!$OMP MASTER
huge_array = ZERO
!$OMP END MASTER
!$OMP CRITICAL
huge_array = huge_array + thread_huge_array
!$OMP END CRITICAL
DEALLOCATE(thread_huge_array)
!$OMP END PARALLEL
COMPLEX(KIND=DBL), ALLOCATABLE :: huge_array(:)
COMPLEX(KIND=DBL), POINTER:: thread_huge_array(:)
INTEGER :: huge_number
ALLOCATE(huge_array(huge_number))
ALLOCATE(thread_huge_array(SIZE(huge_array),omp_get_max_threads()))
thread_huge_array = ZERO
!$OMP PARALLEL PRIVATE (num_thread)
num_thread = omp_get_thread_num()+1
!$OMP DO
DO index1=1,bignumber1
! Some calculations
DO index2=1,bignumber2
! Some calculations
DO index3=1,6
DO index4=1,num_weights_sp
DO index5=1,smallnumber
depending_index = function(index1, index2, index3, index4, index5)
thread_huge_array(depending_index, omp_get_thread_num()) = thread_huge_array(depending_index, omp_get_thread_num())
ENDDO
ENDDO
ENDDO
ENDDO
ENDDO
!$OMP END DO
!$OMP END PARALLEL
huge_array = ZERO
DO index_ii = 1,omp_get_max_threads()
huge_array = huge_array + thread_huge_array(:,index_ii)
ENDDO
DEALLOCATE(thread_huge_array)
DEALLOCATE(huge_array)
因此,通过这种方法,我们可以在8个核之前获得良好的可扩展性,在32个核之前获得合理的可扩展性,而在40个核之前,它比16个核的速度要慢(我们的机器有80个物理核)。当然,我们不能使用reduce子句,因为数组的大小太大,无法放入堆栈中(甚至将ulimit增加到机器允许的最大值)
我们尝试了一种不同的方法:
COMPLEX(KIND=DBL), ALLOCATABLE :: huge_array(:)
COMPLEX(KIND=DBL), ALLOCATABLE :: thread_huge_array(:)
INTEGER :: huge_number, index1, index2, index3, index4, index5, bignumber1, bignumber2, smallnumber, depending_index
ALLOCATE(huge_array(huge_number))
!$OMP PARALLEL FIRSTPRIVATE(thread_huge_array)
ALLOCATE(thread_huge_array(SIZE(huge_array)))
thread_huge_array = ZERO
!$OMP DO
DO index1=1,bignumber1
! Some calculations
DO index2=1,bignumber2
! Some calculations
DO index3=1,6
DO index4=1,6
DO index5=1,smallnumber
depending_index = function(index1, index2, index3, index4, index5)
thread_huge_array(depending_index) = thread_huge_array(depending_index)
ENDDO
ENDDO
ENDDO
ENDDO
ENDDO
!$OMP END DO
!$OMP BARRIER
!$OMP MASTER
huge_array = ZERO
!$OMP END MASTER
!$OMP CRITICAL
huge_array = huge_array + thread_huge_array
!$OMP END CRITICAL
DEALLOCATE(thread_huge_array)
!$OMP END PARALLEL
COMPLEX(KIND=DBL), ALLOCATABLE :: huge_array(:)
COMPLEX(KIND=DBL), POINTER:: thread_huge_array(:)
INTEGER :: huge_number
ALLOCATE(huge_array(huge_number))
ALLOCATE(thread_huge_array(SIZE(huge_array),omp_get_max_threads()))
thread_huge_array = ZERO
!$OMP PARALLEL PRIVATE (num_thread)
num_thread = omp_get_thread_num()+1
!$OMP DO
DO index1=1,bignumber1
! Some calculations
DO index2=1,bignumber2
! Some calculations
DO index3=1,6
DO index4=1,num_weights_sp
DO index5=1,smallnumber
depending_index = function(index1, index2, index3, index4, index5)
thread_huge_array(depending_index, omp_get_thread_num()) = thread_huge_array(depending_index, omp_get_thread_num())
ENDDO
ENDDO
ENDDO
ENDDO
ENDDO
!$OMP END DO
!$OMP END PARALLEL
huge_array = ZERO
DO index_ii = 1,omp_get_max_threads()
huge_array = huge_array + thread_huge_array(:,index_ii)
ENDDO
DEALLOCATE(thread_huge_array)
DEALLOCATE(huge_array)
在最后一种情况下,我们得到了更长的方法时间(由于内存的分配,它要大得多)和更差的相对加速
你能提供一些提示来实现更好的加速吗?或者,使用OpenMP的这些大型阵列是不可能的 请展示一些实际的计时和硬件(什么类型的80核?在什么类型的CPU插槽中?)。有多少个NUMA节点?并不是说代码不可编译,所以我们不能自己做任何测试。请注意,上面的第一种形式有一个bug,因为master不是一个工作共享结构,因此在最后没有一个隐式障碍。另一方面,do是一,所以在它之后不需要屏障,因为构造的末尾自动提供了一个屏障。同样在第二种情况下,为什么不并行最后的加法呢?这与thread_huger_ArrayFladimir的第一个索引非常相似,我使用的机器有80个CPU,每个套接字20个内核和4个NUMA节点。另外,实际情况和不同线程的计时是:1线程132.90 8线程17.58(0.42)| 19.22(0.37)20线程8.33(0.951)| 9.522(0.785)40线程5.89(1.802)| 7.79(1.58)80线程6.748(3.597)| 10.188(3.27)。第一个结果是第一个代码,第二个结果是串行缩减。您可以看到,缩减所花费的时间太长,加速效果更差。在第二个版本中,不使用数组语法,只需在大型数组的元素上写一个循环,然后添加线程版本。这个循环中巨大的_数组的每个元素都是独立的,因此可以并行。此外,您不必在巨大的_数组上循环两次,一次将其归零,一次添加。你可以在这个循环中完成这一切。请展示一些实际的计时和硬件(什么样的80核?在什么样的CPU插槽中?)。有多少个NUMA节点?并不是说代码不可编译,所以我们不能自己做任何测试。请注意,上面的第一种形式有一个bug,因为master不是一个工作共享结构,因此在最后没有一个隐式障碍。另一方面,do是一,所以在它之后不需要屏障,因为构造的末尾自动提供了一个屏障。同样在第二种情况下,为什么不并行最后的加法呢?这与thread_huger_ArrayFladimir的第一个索引非常相似,我使用的机器有80个CPU,每个套接字20个内核和4个NUMA节点。另外,实际情况和不同线程的计时是:1线程132.90 8线程17.58(0.42)| 19.22(0.37)20线程8.33(0.951)| 9.522(0.785)40线程5.89(1.802)| 7.79(1.58)80线程6.748(3.597)| 10.188(3.27)。第一个结果是第一个代码,第二个结果是串行缩减。您可以看到,缩减所花费的时间太长,加速效果更差。在第二个版本中,不使用数组语法,只需在大型数组的元素上写一个循环,然后添加线程版本。这个循环中巨大的_数组的每个元素都是独立的,因此可以并行。此外,您不必在巨大的_数组上循环两次,一次将其归零,一次添加。你可以在这个循环中完成这一切。