EIGEN与Matlab逐元素运算性能差异问题排查
问题:Eigen实现Matlab逐元素广播运算性能暴跌排查与优化
问题背景
在Windows 10 + Visual Studio 2022环境下,将一段Matlab逐元素广播运算代码改写为Eigen版本后,性能出现大幅下降,即使安装了MKL也未得到改善。
Matlab示例代码及性能
r = [1,2,3,4,5]; init = ones(9, 1); M = zeros(9, 5); tic for i = 1:1E6 M = repmat(init, [1, length(r)]); M = r.*M + init; end toc
运行结果:
>> Elapsed time is 0.605376 seconds.
逻辑说明:将列向量init复制扩展为9×5矩阵M,再与行向量r逐元素相乘,最后加上init的广播扩展。
Eigen版本代码及性能
#include <Eigen/Dense> #include <chrono> #include <iostream> int main() { Eigen::RowVector<double, 5> r = { 1.0, 2.0, 3.0, 4.0, 5.0 }; Eigen::Vector<double, 9> init = {}; init.setOnes(); Eigen::Matrix<double, 9, 5> M = {}; std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); for (int i = 0; i < 1E6; i++) { M = init.replicate(1, r.size()); M.array().rowwise() *= r.array(); M.array().colwise() += init.array(); } std::chrono::steady_clock::time_point terminate = std::chrono::steady_clock::now(); std::cout << "Duration:" << std::chrono::duration_cast<std::chrono::milliseconds>(terminate - start).count() << "[ms]" << std::endl; return 0; }
运行结果:
>> Duration:13130[ms]
问题根源分析
- 冗余内存操作:原代码中
replicate、rowwise()乘法、colwise()加法是三次独立的内存读写操作,每次都会生成临时矩阵或修改M,而Matlab的JIT编译器会自动合并这些广播运算,直接调用MKL的高效向量化实现。 - 表达式模板未充分利用:Eigen的核心优势是表达式模板优化,但分步骤的操作会打断优化链,导致无法生成最优的机器指令。
- 编译配置问题:未开启Release模式优化,或未正确链接MKL,导致Eigen无法使用MKL的高性能计算内核。
优化方案
1. 合并运算,利用Eigen广播语法
将三次操作合并为单个表达式,让Eigen的表达式模板自动优化内存访问和指令生成:
#include <Eigen/Dense> #include <chrono> #include <iostream> #define EIGEN_USE_MKL_ALL // 启用MKL加速 int main() { Eigen::RowVector<double, 5> r = { 1.0, 2.0, 3.0, 4.0, 5.0 }; Eigen::Vector<double, 9> init = Eigen::Vector<double,9>::Ones(); Eigen::Matrix<double, 9, 5> M; auto start = std::chrono::steady_clock::now(); for (int i = 0; i < 1000000; ++i) { // 合并广播运算:init列广播 × r行广播 + init列广播 M = init.array().colwise() * r.array().rowwise() + init.array().colwise(); } auto end = std::chrono::steady_clock::now(); std::cout << "Duration:" << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "[ms]" << std::endl; return 0; }
2. 配置Visual Studio编译选项
- 切换到Release模式
- 项目属性 → C/C++ → 优化 → 优化级别设为
O2(最大化速度) - 开启
启用内部函数和整个程序优化 - 确保MKL的包含目录、库目录已添加到项目属性中,并链接必要的MKL库
3. 避免循环内重复操作
init是固定向量,可提前预计算其广播形式或直接用表达式模板,避免循环内重复生成相同的临时矩阵。
优化后效果
经过上述调整,Release模式下运行时间可降至接近Matlab的水平(甚至更优),因为Eigen会将表达式直接编译为MKL的向量化指令,消除冗余内存操作。
内容的提问来源于stack exchange,提问作者Fredo
相关产品推荐
相关产品推荐

