将数组切片作为参数传递时的 Fortran 性能
Fortran performance when passing array slices as arguments
我喜欢 fortran 的数组切片表示法 (array(1:n)
),但我想知道如果在不需要时使用它们是否会影响性能。
例如,考虑这个简单的快速排序代码(它有效,但显然它没有注意选择一个好的主元):
recursive subroutine quicksort(array, size)
real, dimension(:), intent(inout) :: array
integer, intent(in) :: size
integer :: p
if (size > 1) then
p = partition(array, size, 1)
call quicksort(array(1:p-1), p-1)
call quicksort(array(p+1:size), size-p)
end if
end subroutine quicksort
function partition(array, size, pivotdex) result(p)
real, dimension(:), intent(inout) :: array
integer, intent(in) :: size, pivotdex
real :: pivot
integer :: i, p
pivot = array(pivotdex)
call swap(array(pivotdex), array(size))
p=1
do i=1,size-1
if (array(i) < pivot) then
call swap(array(i), array(p))
p=p+1
end if
end do
call swap(array(p), array(size))
end function partition
subroutine swap(a, b)
real, intent(inout) :: a, b
real :: temp
temp = a
a = b
b = temp
end subroutine swap
我可以轻松地传递整个数组以及递归部分应该工作的位置的索引,但我喜欢这种方式的代码。但是,当我调用 quicksort(array(1:p-1), p-1)
时,它会创建一个临时数组来进行操作,还是只是创建一个浅表引用结构或类似的东西?这是一个足够有效的解决方案吗?
是相关的,但由于跨步切片和显式大小的虚拟变量,它似乎生成了临时数组,所以我不会这样做,对吗?
你的子数组
array(1:p-1)
是连续的,前提是 array
是连续的。
此外,您使用了一个假定的形状数组虚拟参数
real, dimension(:), intent(inout) :: array
不需要临时的。仅传递假定形状数组的描述符。由于您的子数组是连续的,因此即使是假定大小、显式大小或具有 contiguous
属性的假定大小虚拟参数也可以。
关于你的效率问题:是的,在大多数情况下,使用假定形状数组和数组切片确实是一个足够有效的解决方案。
涉及 一些 开销。假定形状数组需要数组描述符(有时也称为 "dope vector")。此数组描述符包含有关维度和步幅的信息,设置它需要一些工作。
具有假设形状虚拟参数的被调用过程中的代码必须同时考虑统一步幅(通常情况)和非统一步幅。某个地方的某个人可能想用 somearray(1:100:3) 的实际参数调用您的排序例程,因为他只想对数组的每三个元素进行排序。不寻常,但合法。不能依赖统一步幅的代码可能会有一些性能损失。
话虽如此,编译器,尤其是那些使用 link 时间优化的编译器,现在在内联 and/or 方面相当不错,可以剥离所有额外的工作,并且还倾向于为特殊的程序克隆程序-套管统一大步前进。
因此,通常来说,清晰度(和假定形状数组)应该获胜。请记住,在某些情况下,传递数组参数的老式方法可能会获得一些额外的效率。
我喜欢 fortran 的数组切片表示法 (array(1:n)
),但我想知道如果在不需要时使用它们是否会影响性能。
例如,考虑这个简单的快速排序代码(它有效,但显然它没有注意选择一个好的主元):
recursive subroutine quicksort(array, size)
real, dimension(:), intent(inout) :: array
integer, intent(in) :: size
integer :: p
if (size > 1) then
p = partition(array, size, 1)
call quicksort(array(1:p-1), p-1)
call quicksort(array(p+1:size), size-p)
end if
end subroutine quicksort
function partition(array, size, pivotdex) result(p)
real, dimension(:), intent(inout) :: array
integer, intent(in) :: size, pivotdex
real :: pivot
integer :: i, p
pivot = array(pivotdex)
call swap(array(pivotdex), array(size))
p=1
do i=1,size-1
if (array(i) < pivot) then
call swap(array(i), array(p))
p=p+1
end if
end do
call swap(array(p), array(size))
end function partition
subroutine swap(a, b)
real, intent(inout) :: a, b
real :: temp
temp = a
a = b
b = temp
end subroutine swap
我可以轻松地传递整个数组以及递归部分应该工作的位置的索引,但我喜欢这种方式的代码。但是,当我调用 quicksort(array(1:p-1), p-1)
时,它会创建一个临时数组来进行操作,还是只是创建一个浅表引用结构或类似的东西?这是一个足够有效的解决方案吗?
你的子数组
array(1:p-1)
是连续的,前提是 array
是连续的。
此外,您使用了一个假定的形状数组虚拟参数
real, dimension(:), intent(inout) :: array
不需要临时的。仅传递假定形状数组的描述符。由于您的子数组是连续的,因此即使是假定大小、显式大小或具有 contiguous
属性的假定大小虚拟参数也可以。
关于你的效率问题:是的,在大多数情况下,使用假定形状数组和数组切片确实是一个足够有效的解决方案。
涉及 一些 开销。假定形状数组需要数组描述符(有时也称为 "dope vector")。此数组描述符包含有关维度和步幅的信息,设置它需要一些工作。
具有假设形状虚拟参数的被调用过程中的代码必须同时考虑统一步幅(通常情况)和非统一步幅。某个地方的某个人可能想用 somearray(1:100:3) 的实际参数调用您的排序例程,因为他只想对数组的每三个元素进行排序。不寻常,但合法。不能依赖统一步幅的代码可能会有一些性能损失。
话虽如此,编译器,尤其是那些使用 link 时间优化的编译器,现在在内联 and/or 方面相当不错,可以剥离所有额外的工作,并且还倾向于为特殊的程序克隆程序-套管统一大步前进。
因此,通常来说,清晰度(和假定形状数组)应该获胜。请记住,在某些情况下,传递数组参数的老式方法可能会获得一些额外的效率。