在Fortran中有效地移动多维数组

时间:2014-09-18 15:30:20

标签: arrays fortran

我有几个不同大小的四维数组:

array_one(1:2,1:xm,1:ym,1:zm)

其中current_step = 1previous_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,:,:,:)

这种转变的有效方式是什么?

2 个答案:

答案 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。

结果

Copy results

每个副本的平均时间在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