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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 04:14:52