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

为何Eigen显式循环矩阵乘法远慢于Fortran?

为什么Eigen显式循环矩阵乘法比Fortran慢3倍?

这个问题我太熟了!核心原因是Fortran和C++编译器对显式循环的优化逻辑差异,再加上你写的循环和Eigen默认的内存存储顺序不匹配,直接导致缓存命中率暴跌,速度自然慢了好几倍。下面具体拆解:

1. 内存存储顺序和循环访问不匹配,拖垮缓存效率

  • Fortran天生是列优先(Column-Major)存储数组:你的Fortran循环里,计算c(i,j) = sum(a(i,k)*b(k,j))时,b(k,j)在j固定的情况下,k遍历的是同一列的元素,内存是连续的——这对缓存友好得不行。就算a(i,k)是跨列访问,gfortran在-O3优化下,会自动做循环交换(比如把i和k的顺序调换),强行把内存访问改成连续模式,缓存命中率直接拉满。
  • Eigen的MatrixXd默认是**行优先(Row-Major)**存储:你的C循环里,b(k,j)在j固定时,k遍历的是不同行的第j个元素,内存是跳着读的——这会触发大量缓存 miss(缓存里没有要读的数据,得去内存里取),速度直接打折扣。g虽然也会尝试优化,但面对这种天生不匹配的访问模式,优化力度远不如gfortran。

2. Fortran编译器对数值循环的优化更激进

gfortran针对数值计算的优化是出了名的狠:在-O3和-march=native下,它会自动给显式循环做向量化、循环展开、循环融合,甚至直接把你写的三层循环替换成类似matmul的高效实现——说白了,你写的Fortran循环,编译器可能已经悄悄帮你改成了接近内置函数的版本。

而g面对C的显式循环,虽然也有优化,但C++语法太灵活,编译器要处理的场景太多,对这种特定数值循环的针对性优化就没那么狠了,自然干不过gfortran。

3. 怎么让Eigen的显式循环速度追上Fortran?

试试这两个调整,保证速度能上来:

  • 改用Eigen的列优先矩阵:
    Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor> a(n,n), b(n,n), c(n,n);
    
  • 调整循环顺序,让内存访问连续:
    // 改成i→k→j的顺序,让a和b的访问都连续
    for(int i=0;i<n;i++){
      for(int k=0;k<n;k++){
        double ak_val = a(i,k);
        for(int j=0;j<n;j++){
          c(i,j) += ak_val * b(k,j);
        }
      }
    }
    
    这样调整后,缓存命中率会大幅提升,g++的优化就能发挥作用,速度基本能追上Fortran的循环实现。

至于两者的内置实现(Fortran的matmul和Eigen的*)速度相近,是因为它们都调用了高度优化的BLAS/LAPACK底层实现(或者编译器内置的手工优化矩阵乘法),这些都是专家写的极致优化代码,和我们手动写的循环根本不在一个量级。

内容的提问来源于stack exchange,提问作者Clock Su

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:11:04