Fortran中对n维数组的m个维度求和的最优简洁实现方法
Fortran多维度数组求和的优化实现与性能对比
你提到的链式调用SUM确实在维度变多后可读性拉胯,而且性能表现也不尽人意,下面给你梳理几个更优的实现方式,再结合你做的性能测试聊聊实际表现:
替代实现方案
1. 嵌套DO循环
手动遍历需要保留的维度,对每个位置的目标维度求和,逻辑直观,还能避免链式SUM产生的临时数组开销:
DO l = 1,size DO j = 1,size res12(j,l) = SUM(array(:,j,:,l)) END DO END DO
2. FORALL语句
比嵌套DO更简洁,语法上更贴近“批量操作”的思路,实际性能和DO循环不相上下:
FORALL (j = 1:size, l = 1:size) res13(j,l) = SUM(array(:,j,:,l))
3. DO CONCURRENT(Fortran 2008+)
如果你的编译器支持较新的Fortran标准,DO CONCURRENT是更现代的选择,它允许编译器自动做并行优化,写法和FORALL类似:
DO CONCURRENT (j = 1:size, l = 1:size) res14(j,l) = SUM(array(:,j,:,l)) END DO
不过你测试里用的g95不支持这个特性,所以没纳入测试范围。
性能测试结果与分析
你在不同编译器和系统上的测试结果很有参考性,整理后关键结论如下:
Mac g95编译器
第二、四维度求和:
链式SUM:0.193214
嵌套DO循环:0.140472
FORALL:0.18884899第三、四维度求和:
链式SUM:0.196938
嵌套DO循环:0.114286005
FORALL:0.115414
Mac gfortran-mp-7编译器
第二、四维度求和:
链式SUM:0.279830009
嵌套DO循环:0.131999999
FORALL:0.130150005第三、四维度求和:
链式SUM:3.01672006
嵌套DO循环:0.111460000
FORALL:0.110610001
通用结论
- 链式SUM性能最差:完全符合预期,因为每次调用
SUM都会创建临时数组,多维度求和时临时数组的创建与销毁会带来额外开销; - 嵌套DO与FORALL性能相近:两者可以根据个人编码偏好选择,喜欢结构化逻辑的用DO,偏好简洁批量风格的用FORALL;
- 编译器差异明显:比如gfortran在对第3、4维度做链式求和时性能暴跌,推测是编译器处理临时数组的机制导致的,这也侧面说明链式SUM的稳定性不如手动循环。
内容的提问来源于stack exchange,提问作者Thomas Kühn
相关产品推荐
相关产品推荐

