Eigen库double运算速度远超float的原因及Intel编译器相关疑问
Eigen中double与float运算性能及编译器差异测试
我在Eigen库中分别实现了double和float类型的3x3矩阵向量乘加运算,将两组运算在循环中执行多次并统计耗时,发现double类型运算速度显著快于float——在无优化、O1、O2等不同编译优化级别下,double运算速度均为float的2-3倍,且在不同处理器上结论一致。
初始测试代码
#include <iostream> #include <Eigen/Dense> #include <chrono> Eigen::Matrix3d A1 = Eigen::Matrix3d::Random(); Eigen::Matrix3d B1 = Eigen::Matrix3d::Random(); Eigen::Vector3d C1 = Eigen::Vector3d::Random(); Eigen::Vector3d D1 = Eigen::Vector3d::Random(); Eigen::Matrix3f A2 = Eigen::Matrix3f::Random(); Eigen::Matrix3f B2 = Eigen::Matrix3f::Random(); Eigen::Vector3f C2 = Eigen::Vector3f::Random(); Eigen::Vector3f D2 = Eigen::Vector3f::Random(); int num = 3000000; int main() { std::cout << "start1:" << std::endl; auto start1 = std::chrono::high_resolution_clock::now(); for (int i = 0; i < num; i++) { Eigen::Vector3d E1 = A1 * C1 + B1 * D1; double dd = E1[0, 0];//just prevent compiler optimization } auto end1 = std::chrono::high_resolution_clock::now(); std::chrono::duration<double, std::milli> elapsed1 = end1 - start1; std::cout << "Eigen use: " << elapsed1.count() << " ms." << std::endl; std::cout << "start2:" << std::endl; auto start2 = std::chrono::high_resolution_clock::now(); for (int i = 0; i < num; i++) { Eigen::Vector3f E2 = A2 * C2 + B2 * D2; float dd = E2[0, 0];//just prevent compiler optimization } auto end2 = std::chrono::high_resolution_clock::now(); std::chrono::duration<double, std::milli> elapsed2 = end2 - start2; std::cout << "Eigen use: " << elapsed2.count() << " ms." << std::endl; }
初始测试结果(i7-13700k、Win10 X64、VS2022 X64 O2优化)
double:
Eigen use: 38.4053 ms.float:
Eigen use: 92.1853 ms.
之后改用Google Benchmark库改写测试代码,分别用MSVC X64和Intel C++编译器编译运行,针对大量3x3小矩阵计算,发现MSVC下double仍比float快,但Intel编译器下两者速度接近,且Intel编译器编译的程序远快于MSVC版本。
Benchmark测试代码
#include <iostream> #include <Eigen/Dense> #include <benchmark/benchmark.h> Eigen::Matrix3d A1 = Eigen::Matrix3d::Random(); Eigen::Matrix3d B1 = Eigen::Matrix3d::Random(); Eigen::Vector3d C1 = Eigen::Vector3d::Random(); Eigen::Vector3d D1 = Eigen::Vector3d::Random(); Eigen::Matrix3f A2 = Eigen::Matrix3f::Random(); Eigen::Matrix3f B2 = Eigen::Matrix3f::Random(); Eigen::Vector3f C2 = Eigen::Vector3f::Random(); Eigen::Vector3f D2 = Eigen::Vector3f::Random(); static void Floats(benchmark::State & state) { for (auto _ : state) { Eigen::Vector3f E2 = A2 * C2 + B2 * D2; float dd = E2(0, 0); benchmark::DoNotOptimize(E2); benchmark::DoNotOptimize(dd); } } BENCHMARK(Floats)->Iterations(50000000); static void Doubles(benchmark::State & state) { for (auto _ : state) { Eigen::Vector3d E1 = A1 * C1 + B1 * D1; double dd = E1(0, 0); benchmark::DoNotOptimize(E1); benchmark::DoNotOptimize(dd); } } BENCHMARK(Doubles)->Iterations(50000000);
MSVC X64测试结果
Run on (8 X 3418 MHz CPU s) CPU Caches: L1 Data 48 KiB (x8) L1 Instruction 32 KiB (x8) L2 Unified 2048 KiB (x8) L3 Unified 30720 KiB (x1) ---------------------------------------------------------------------- Benchmark Time CPU Iterations ---------------------------------------------------------------------- Floats/iterations:50000000 51.0 ns 51.2 ns 50000000 Doubles/iterations:50000000 19.2 ns 19.1 ns 50000000
Intel C++编译器X64测试结果
Run on (8 X 3418 MHz CPU s) CPU Caches: L1 Data 48 KiB (x8) L1 Instruction 32 KiB (x8) L2 Unified 2048 KiB (x8) L3 Unified 30720 KiB (x1) ---------------------------------------------------------------------- Benchmark Time CPU Iterations ---------------------------------------------------------------------- Floats/iterations:50000000 2.18 ns 2.19 ns 50000000 Doubles/iterations:50000000 2.22 ns 2.19 ns 50000000
问题解答
1. 为何Eigen库中double运算速度远超float?
- 指令集与寄存器利用率:现代x86-64 CPU的SIMD指令集(如AVX、AVX2)对double的支持更适配小矩阵场景。256位YMM寄存器可容纳4个double或8个float,但3x3矩阵向量乘的运算逻辑更贴合double的寄存器布局,减少了数据对齐、指令调度的额外开销。
- Eigen的模板特化优化:Eigen对小尺寸矩阵(如3x3)有专门的模板特化实现,double版本的特化代码更紧凑,避免了float版本中可能存在的冗余检查或类型转换操作。
- 编译器代码生成差异:MSVC对double的SIMD优化策略更激进,为小矩阵计算生成了更高效的指令组合,而float版本的优化未充分挖掘硬件潜力,导致指令数更多、延迟更高。
2. 为何Intel编译器编译的程序远快于MSVC版本?
- Intel CPU深度适配:Intel C++编译器是为自家CPU量身定制的,能充分利用其微架构特性(如缓存层次、指令流水线、SIMD扩展),生成更贴合硬件的机器码,比如针对3x3矩阵计算直接生成紧凑的AVX2/FMA指令序列。
- Eigen与Intel编译器的适配优化:Eigen内置了针对Intel编译器的优化分支,启用了专属优化宏,解锁了更高效的矩阵运算实现,而MSVC的优化逻辑更通用,未针对Intel最新CPU做深度定制。
- 循环与指令调度优化:Intel编译器在循环展开、指令重排、寄存器分配上的策略更激进,能将重复的小计算任务完全展开到寄存器中,消除循环开销,而MSVC的循环优化相对保守。
3. Intel编译器是否会导致计算结果不精确?
默认情况下不会。Intel编译器的默认浮点运算模式遵循IEEE 754标准,与MSVC默认行为一致,计算精度无明显差异。
只有手动开启特定优化选项(如-fast)时,才会启用非严格IEEE兼容的优化(如调整运算顺序、忽略部分浮点异常),但这类选项是可选的,默认不会开启。保持默认浮点精度设置时,计算结果与MSVC编译版本在误差范围内一致。
内容的提问来源于stack exchange,提问作者STENoFreedSpeech
相关产品推荐
相关产品推荐

