我有几个不同大小的四维数组:
array_one(1:2,1:xm,1:ym,1:zm)
其中current_step = 1
和previous_step = 2
。
在一个长循环中,有许多其他操作,我需要将current_step值转换为previous_step,如:
array_one(previous_step,:,:,:) = array_one(current_step,:,:,:)
我知道我可以在DO循环中做到这一点,但也许它不是最有效的方法。由于我至少有24个这样的阵列,每个阵列都有不同的大小(即xm,ym,zm),所以我需要为它们中的每一个运行单独的DO循环,这可能使它变慢。
我以下列方式失败了:
array_one(previous_step,:,:,:) = array_one(current_step,:,:,:)
这种转变的有效方式是什么?
答案 0 :(得分:2)
我在我的系统上运行了一个简单的基准测试,使用8种不同的方法来复制数组。我测试了两种基本形式的副本:
do k=1,nx
do j=1,nx
do i=1,nx
array(2,i,j,k) = array(1,i,j,k)
end do
end do
end do
和
array(2,:,:,:) = array(1,:,:,:)
对于其中的每一个,我还使用t
索引作为最后一个数组索引进行了测试,例如:
array(i,j,k,2) = array(i,j,k,1)
和
array(:,:,:,2) = array(:,:,:,1)
最后,我按照连续的方式和openmp指令测试了这4个副本中的每一个,例如
!$omp parallel do shared(array) private(i,j,k)
...
!$omp end parallel do
表示do
循环副本和
!$omp parallel workshare shared(array)
...
!$omp end parallel workshare
用于数组切片复制。
对于尺寸为100x100x100x2到1000x1000x1000x2的每个阵列,每个副本执行100次,增量为100(对于所有测试的阵列,ni = nj = nk)。
我使用gfortran 4.9.1进行了测试,并使用
编译了我的测试用例gfortran -march=native -fopenmp -O3 -o arraycopy arraycopy.f90
我的CPU是intel i7 990x(启用HT的6核),native
将瞄准芯片支持的最高指令集。 OpenMP将产生12个线程。
操作系统是Linux 3.12.13。
每个副本的平均时间在y轴上,阵列尺寸在x轴上(例如500是500x500x500x2或2x500x500x500阵列)。红线是do循环复制(虚线是最后带t
索引的变体)。绿线是数组切片副本(虚线是最后带t
索引的变体)。对于两个串行副本,t
索引的变体首先更快(我没有调查原因),并且数组符号复制比循环更快。蓝线是首先使用t
索引的openmp副本。黑线是最后一个t
索引的openmp副本。并行do和并行工作共享结构的性能是等效的。
使用典型的编译标志在您自己的系统上运行您自己的基准测试。这里的结果将特定于我的系统,包括优化标志,SIMD指令和带有12个线程的OpenMP。对于具有较少内核的系统和具有较少或较大指令集的CPU(例如,具有AVX2的CPU应该执行得更好),该将不同。这些结果还受缓存局部性,RAM和总线速度以及我的OS调度程序如何处理超线程的影响。
对于我在我的系统上的结果,我会使用数组切片表示法来进行串行拷贝,为了获得最佳性能,我会使用OpenMP。
答案 1 :(得分:1)
简而言之,当一个程序发出一个内存读取操作时,比如A(i)
,它不仅会读取A(i)
,而是读取A(i-2), A(i-1), A(i), A(i+1), A(i+2)
之类的内容。然后,这些值将存储在CPU缓存中,这是一个更快的内存。也就是说,CPU将读取一块内存并将其放入缓存中供以后使用。此优化基于以下事实:您的下一个操作很可能会使用其中一些周围值。如果是这种情况,CPU就不再需要再次获取内存,这是一项非常昂贵的操作(比浮点运算贵100倍),而只需要寻找缓存中的值。这称为数据位置。
在Fortran中,多维数组存储在列主要顺序中。例如,假设您有以下2x2矩阵:
A(1,1)=a11, A(1,2)=a12, A(2,1)=a21, A(2,2)=a22
。
矩阵A(1:2,1:2)
按以下顺序线性存储在内存中:a11, a21, a12, a22
(相反,在行 - 主要顺序中,如C语言,顺序为{ {1}})。您可以推断出更高维度的订单。
简而言之,Fortran数组从从左到右线性存储在内存中。如果要利用数据局部性,则需要从从左到右遍历数组。
简答:我认为您应该将结构更改为a11, a12, a21, a22
,如果您要循环遍历数组,请按以下方式执行:
(1:xm,1:ym,1:zm,1:2)
此外,执行do h = 1, 2
do i = 1, zm
do j = 1, ym
do k = 1, xm
A[k,j,i,h] = *...something...*
end do
end do
end do
end do
和等效的do循环之间的区别在于A(:)=B(:)
等同于A(:)=B(:)
语句:
forall
此处更多http://en.wikipedia.org/wiki/Fortran_95_language_features#The_FORALL_Statement_and_Construct