Fortran中子程序传数组:假定形状与显式形状的性能对比
Fortran中假定形状数组与显式形状数组的性能对比
问题背景
在Fortran中向子程序传递数组时,从运行速度和内存占用两个维度,假定形状数组与显式形状数组哪种实现方式更优?
示例程序
! Compile with ! ifort /O3 main.f90 -o run_win.exe module mymod USE iso_Fortran_env, ONLY: dp => real64 implicit none private public :: dp, sub_trace, sub_trace_es contains subroutine sub_trace(mat,trace) ! 假定形状数组版本 implicit none real(dp), intent(in) :: mat(:,:) real(dp), intent(out) :: trace real(dp) :: V(size(mat,dim=1)) integer :: i,N if (size(mat,dim=1) /= size(mat,dim=2)) then error stop "输入矩阵不是方阵!" endif N = size(mat,dim=1) do i=1,N V(i) = mat(i,i) enddo trace = sum(V) end subroutine sub_trace subroutine sub_trace_es(n,mat,trace) ! 显式形状数组版本 implicit none integer, intent(in) :: n real(dp), intent(in) :: mat(n,n) real(dp), intent(out) :: trace real(dp) :: V(n) integer :: i do i=1,n V(i) = mat(i,i) enddo trace = sum(V) end subroutine sub_trace_es end module mymod program main use mymod, only: dp, sub_trace,sub_trace_es implicit none integer, parameter :: nn=2 real(dp) :: mat(nn,nn) real(dp), allocatable :: mat4(:,:) real(dp) :: trace1,trace2,trace3,trace4 write(*,*) "子程序数组传递测试:" write(*,*) "假定形状 vs 显式形状" mat(1,:) = [2_dp,3_dp] mat(2,:) = [4_dp,5_dp] call sub_trace(mat,trace1) write(*,*) "trace1 = ", trace1 call sub_trace_es(nn,mat,trace2) write(*,*) "trace2 = ", trace2 ! francescalus提供的第一个示例:显式形状可能截断数组 call sub_trace_es(2,real([1,2,3,4,5,6,7,8,9],dp), trace3) write(*,*) "trace3 = ", trace3 ! 第二个示例:假定形状处理动态分配数组 mat4 = reshape(real([1,2,3,4,5,6,7,8,9],dp),[3,3]) call sub_trace(mat4, trace4) write(*,*) "trace4 = ", trace4 pause end program
性能对比分析
1. 运行速度
- 显式形状数组:如果数组大小是编译时常量,编译器可提前做深度优化(如循环展开、内存访问模式优化),此时性能会有微弱优势;若大小为运行时传入参数,编译器优化空间与假定形状数组基本一致。
- 假定形状数组:调用时需传递包含维度、边界、步长等信息的数组描述符,会带来极小调用开销,但现代编译器在O3等高级优化下,该开销几乎可忽略。
- 实际场景:日常编程中两者速度差异无法感知;仅在极端高频调用的子程序中,显式形状可能有可测量的微弱优势。
2. 内存占用
- 显式形状数组:无需额外存储数组描述符,内存占用完全等于原数组大小;若为编译时常量大小的数组,编译器可优化为栈分配,进一步降低内存管理开销。
- 假定形状数组:需额外内存存储数组描述符,但描述符仅占几十字节,对大数组而言额外开销可完全忽略;仅处理极小数组时占比稍高,但实际影响微乎其微。
- 额外优势:假定形状数组可通过
size()等函数做边界检查(如示例中判断是否为方阵),避免显式形状数组可能出现的截断、越界访问问题,安全性更优。
总结
- 绝大多数场景下,两者性能(速度+内存)差异可忽略,优先选择假定形状数组——代码更简洁,无需手动传递大小参数,支持动态大小/可分配数组,安全性更高。
- 若对性能极致敏感且数组大小为编译时常量,显式形状数组可能带来微弱性能提升。
内容的提问来源于stack exchange,提问作者Alessandro
相关产品推荐
相关产品推荐

