Fortran中用size函数获取数组大小的性能及代码改造疑问
核心结论
使用size(ar)获取假定形状数组的大小,性能开销可以忽略不计,哪怕子程序被调用百万次,也不会成为性能瓶颈,完全可以放心使用。
为什么size()开销极小?
Fortran的假定形状数组(如示例中的ar(:))在传递给子程序时,编译器会自动传递一份数组的描述元数据(包含各维度的大小、起始地址、步长等信息)。size()函数并非像某些语言那样遍历数组计算长度,而是直接读取这份已经存在的元数据——本质上和读取一个普通整数变量的开销几乎一致。
更关键的是,现代Fortran编译器(如GCC、Intel Fortran)在开启优化选项(-O2或-O3)时,会直接将size(ar)的结果内联到代码中,连函数调用的开销都能消除,性能和直接传递nelem参数的方法完全持平,甚至可能更优(避免了参数传递的冗余)。
两种方法的对比
方法1(传递nelem参数):
旧版本Fortran的传统写法,但需要手动维护nelem和数组实际大小的一致性,容易出现参数不匹配的bug,且大规模修改代码时成本极高。
示例代码:subroutine asub(nelem,ar) integer,intent(in)::nelem real*8,intent(in)::ar(:) ! do stuff with nelem such as allocate other arrays end subroutine asub方法2(使用size()函数):
F95及以后的推荐写法,代码更简洁,完全避免了参数不一致的风险,性能上和方法1无差异,同时大幅降低了代码维护成本。
示例代码:subroutine asub(ar) real*8,intent(in)::ar(:) ! do stuff with size(ar) such as allocate other arrays end subroutine asub
针对你的代码维护场景
你提到代码中用全局变量存储数组大小的做法不合理,这一点完全正确——全局变量会增加代码耦合度,引发难以排查的bug。而采用size()函数的方式,不需要在整个代码中大量修改来传播数组大小,只需直接替换全局变量的引用即可,既解决了全局变量的问题,又没有性能顾虑,是最优的解决方案。
额外建议
如果仍有疑虑,可以自行编写简单的性能测试:写一个循环调用目标子程序百万次,分别用两种方法实现,对比运行时间。实际测试后你会发现,两者的性能差异微乎其微,完全可以忽略。
内容的提问来源于stack exchange,提问作者naffrancois

