【发布时间】:2015-09-03 20:05:30
【问题描述】:
在 Fortran90 中操作和分配多维数组中的子数组时,我偶然发现了一个有趣的性能怪癖。
Fortran90 引入了操作数组子部分的能力,我看到一些地方建议使用这种“切片”方法而不是循环来执行数组操作。例如,如果我必须添加两个数组,a 和 b,大小为 10,最好这样写:
c(1:10) = a(1:10) + b(1:10)
或
c = a + b
代替
do i = 1, 10
c(i) = a(i) + b(i)
end do
我对简单的一维和二维数组尝试了这种方法,发现使用“切片”表示法更快。然而,当在多维数组中分配这样的结果时,事情开始变得有点有趣了。
首先,我必须为我相当粗暴的性能测量练习道歉。我什至不确定我采用的方法是否是时间和测试代码的正确方法,但我对测试的定性结果相当有信心。
program main
implicit none
integer, parameter :: mSize = 10000
integer :: i, j
integer :: pCnt, nCnt, cntRt, cntMx
integer, dimension(mSize, mSize) :: a, b
integer, dimension(mSize, mSize, 3) :: c
pCnt = 0
call SYSTEM_CLOCK(nCnt, cntRt, cntMx)
print *, "First call: ", nCnt-pCnt
pCnt = nCnt
do j = 1, mSize
do i = 1, mSize
a(i, j) = i*j
b(i, j) = i+j
end do
end do
call SYSTEM_CLOCK(nCnt, cntRt, cntMx)
print *, "Created Matrices: ", nCnt-pCnt
pCnt = nCnt
! OPERATIONS BY SLICING NOTATION
!c(1:mSize, 1:mSize, 1) = a + b
!c(1:mSize, 1:mSize, 2) = a - b
!c(1:mSize, 1:mSize, 3) = a * b
! OPERATIONS WITH LOOP
do j = 1, mSize
do i = 1, mSize
c(i, j, 1) = a(i, j) + b(i, j)
c(i, j, 2) = a(i, j) - b(i, j)
c(i, j, 3) = a(i, j) * b(i, j)
end do
end do
call SYSTEM_CLOCK(nCnt, cntRt, cntMx)
print *, "Added Matrices: ", nCnt-pCnt
pCnt = nCnt
end program main
可以看出,我有两种方法可以对两个大型 2D 数组进行操作并将其分配到一个 3D 数组中。我非常赞成使用切片符号,因为它可以帮助我编写更短、更优雅的代码。但是在观察到我的代码有多么缓慢时,我不得不重新检查切片符号相对于循环内计算的能力。
我使用适用于 Ubuntu 14.04 的 GNU Fortran 4.8.4 运行带有和不带有 -O3 标志的上述代码
-
没有 -O3 标志
一个。切片符号
5 Runs - 843, 842, 842, 841, 859 Average - 845.4b.循环计算
5 Runs - 1713, 1713, 1723, 1711, 1713 Average - 1714.6 -
带有 -O3 标志
一个。切片符号
5 Runs - 545, 545, 544, 544, 548 Average - 545.2b.循环计算
5 Runs - 479, 477, 475, 472, 472 Average - 475
我发现非常有趣的是,如果没有 -O3 标志,切片符号的性能仍然比循环好得多。但是,使用 -O3 标志会导致这种优势完全消失。相反,在这种情况下使用数组切片表示法是有害的。
事实上,对于我相当大的 3D 并行计算代码,这将成为一个重要的瓶颈。我强烈怀疑在将低维数组分配给高维数组期间形成临时数组是罪魁祸首。但是为什么优化标志在这种情况下没有优化赋值呢?
而且,我觉得责备-O3标志不是一件值得尊敬的事情。那么数组临时对象真的是罪魁祸首吗?还有什么我可能会丢失的吗?任何见解都将对加快我的代码速度非常有帮助。谢谢!
【问题讨论】:
-
您能使用更新版本的 gfortran 吗?
-
查看
-O3暗示的选项。如果您询问,编译器可以进行积极的循环展开,这可能就是您所看到的(例如,您的循环案例不再是循环)。 -
在这种情况下,我认为编译器不需要制作临时数组。
-
另外,您应该初始化变量并打印结果。否则,我的 gfortran 只报告第二部分需要 0 毫秒,因为它在时间检查之前对其进行了优化或移动。
-
@AlexanderVogt 我可以使用更新版本的 gfortran,但最后,我必须将代码提交给我的大学集群,而我的 fortran 版本实际上不在我的控制范围内。我首先在集群上观察到了这种速度差异。
标签: performance multidimensional-array fortran