You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 07:01:03