Fortran中基于BLAS实现高阶张量缩并时如何加速reshape操作?
核心问题定位
你基于DGEMM实现张量缩并的流程设计是完全合理的,当前reshape性能不佳的本质原因是:Fortran内置的reshape函数默认会生成独立的数组拷贝,而非创建共享内存的数组视图,大尺寸张量下的全量拷贝会产生非常可观的额外开销。
之前尝试的其他数组阶数调整方案报错,几乎都是没有对齐Fortran列优先的存储规则、未正确声明指针维度参数导致的,以下是可直接落地的无拷贝实现方案:
无拷贝阶数调整实现
利用Fortran 2003及以上版本的指针绑定功能,可实现零拷贝的数组形状调整,核心逻辑是只要两个数组的总元素数、内存排列顺序完全匹配,就可以通过指针直接绑定内存地址,不需要拷贝数据:
- 针对
B[b,c,d]降维为D[b,e](e = c*d)的需求:
因为Fortran为列优先存储,B的内存排列(第一维b最快、第二维c次之、第三维d最后)和D的内存排列(第一维b最快、第二维e慢进)完全一致,直接绑定即可:use iso_fortran_env, only: dp => real64 real(dp), dimension(:,:,:), target :: B ! 原B数组,必须声明为target属性 real(dp), dimension(:,:), pointer :: D ! 显式指定D的形状,直接绑定B的内存地址,无拷贝 D(1:size(B,1), 1:size(B,2)*size(B,3)) => B - 针对DGEMM输出
E[a,e]升维为C[a,c,d]的需求:E的内存排列(第一维a最快、第二维e慢进)和目标张量C的内存排列完全匹配,绑定方式和上述逻辑一致:real(dp), dimension(:,:), target :: E ! DGEMM输出的E数组,需声明为target属性 real(dp), dimension(:,:,:), pointer :: C C(1:size(E,1), 1:size(B,2), 1:size(B,3)) => E
常见报错规避
之前自行实现指针方案时的高频报错原因及修正方式:
- 未给原数组添加
target属性:Fortran规定只有被声明为target的数组才能被指针绑定,否则会触发编译错误。 - 存储顺序不匹配:如果误按行优先规则拆分维度,会导致内存访问错位,触发段错误。只要两个数组的总元素数相等、维度拆分符合列优先规则,就不需要额外转置操作。
- 编译标准不匹配:编译时需开启Fortran 2003及以上标准支持,例如gfortran编译时添加
-std=f2003参数,Intel Fortran默认已支持该语法。 - 老版本编译器兼容:如果需要兼容不支持形状重绑定的老旧编译器,可改用
<iso_c_binding>模块的c_f_pointer接口实现绑定,逻辑完全一致。
DGEMM适配说明
通过上述方式生成的数组视图和原数据共享同一块内存,仅做了形状标记的调整,没有额外拷贝开销,可直接传入DGEMM接口使用:
- 因为视图的内存排列和Fortran默认的列优先规则完全匹配,DGEMM的转置标记、维度参数都不需要调整,按常规矩阵乘法调用即可。
- 注意:由于是共享内存视图,后续如果修改视图的元素值,会直接修改原数组的内存内容。如果需要保留原数据,再考虑显式拷贝操作。
内容的提问来源于stack exchange,提问作者AlphaF20
相关产品推荐
相关产品推荐

