eigen矩阵乘法效率远低于环路遍历

问题描述 投票:0回答:1
#include <Eigen/Dense>
#include <iostream>
#include <random>
#include <chrono>

int main()
{
    const int num_points = 300000000;

    Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> init_points(num_points, 3);
    Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> transformed_points(num_points, 3);

    std::mt19937 rng(42); 
    std::uniform_real_distribution<float> dist(-100.0f, 100.0f);

    for (int i = 0; i < num_points; ++i)
    {
        init_points(i, 0) = dist(rng); // x
        init_points(i, 1) = dist(rng); // y
        init_points(i, 2) = dist(rng); // z
    }

    float theta = 3.14159265358 / 4; // pi/4
    Eigen::Matrix3f rotation;
    rotation = Eigen::AngleAxisf(theta, Eigen::Vector3f::UnitZ());

    Eigen::Vector3f translation(10.0f, 20.0f, 30.0f);

    auto start_time = std::chrono::high_resolution_clock::now();

    //transformed_points = init_points * rotation;   //uncomment this line to use the Matrix multiply version
    //transformed_points.rowwise() += translation.transpose(); //uncomment this line to use the Matrix multiply version

    for (int i = 0; i < num_points; ++i) //comment this for loop to use the Matrix multiply version
    {
        Eigen::Vector3f v = init_points.row(i).transpose();
        v = rotation * v;
        v += translation;
        transformed_points.row(i) = v.transpose();
    }

    auto end_time = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double, std::milli> duration_ms = end_time - start_time;
    std::cout << "total consume: " << duration_ms.count() << "ms" << std::endl;

    std::cout << "first 5 points:(x,y,z)" << std::endl;
    for (int i = 0; i < 5; ++i)
    {
        std::cout << "("<< transformed_points(i, 0) << ","<< transformed_points(i, 1) << ", " << transformed_points(i, 2) << ")" << std::endl;
    }

    return 0;
}

I当前在通过点云采样获得的对象上执行坐标转换。我需要先旋转一组点,然后翻译它们以获得转换的点云。

这个点云数据总共包含300,000,000点,我正在使用eigen的动态阵列进行存储。

上面的代码是一个测试示例,我在其中初始数字初始化了300,000,000点,然后执行旋转和翻译操作。该代码既包括循环遍历版本和矩阵乘法版本。要使用矩阵乘法,您只需要删除我发表评论的两行并评论前循环部分。 the是对循环遍历和矩阵乘法版本所消耗的时间的比较:

1。对于旋转和翻译操作,我尝试将所有300,000,000点乘以旋转矩阵,然后添加翻译向量。此操作在我的i7-13700k cpu上大约需要

2044ms

2。我写了一个循环遍历每个点并逐一旋转和翻译。总时间仅为600ms我知道eigen使用许多CPU指令集来优化矩阵乘法,并且我正在使用启用了各种SIMD和AVX优化的英特尔ICC编译器。为什么在这里的前横梁比矩阵乘法快3倍?

加上,是否有任何空间可以进一步优化上述代码? 有人可以帮助分析吗?我非常感谢它。 PS:我确实有优化的打开,因为在优化的情况下关闭了所花费的时间比现在要高得多。我的意思是,当我打开优化时,for循环比单独进行矩阵乘法更快

我刚刚进行了一些时间测量。由于我没有与您相同的CPU,并且您没有指定所使用的确切编译器选项,因此效果可能会或可能不会有所不同。 我测试了: Intel(R)Oneapi DPC ++/C ++编译器2025.0.4

编译器标志:-dndebug -o3 -march =i7-9850H

上的本机 以下所有值是3个测量值的中间:

逐行的弯曲率花费了1247.91ms。 矩阵乘版本最初采用2583.93ms。 使用Noalias(如

transformed_points.noalias() = init_points * rotation;

中)时,矩阵乘版本为1451.23ms。

noalias

有助于摆脱临时性,请参阅
c++ eigen matrix-multiplication eigen3
1个回答
0
投票

当将行和列切换时,将固定尺寸从
Eigen::Dynamic
切换到3时,iThe矩阵乘法版本花费了1278.85ms。由于eigen默认为列是列 - 这意味着每个点的x,y和z现在彼此隔绝。 其他优化可能是可能的,但至少现在是初始的逐行环和矩阵乘法所需(几乎)运行时间的时间。 这是最终代码:

const int num_points = 300000000; Eigen::Matrix<float, 3, Eigen::Dynamic> init_points(3, num_points); Eigen::Matrix<float, 3, Eigen::Dynamic> transformed_points(3, num_points); std::mt19937 rng(42); std::uniform_real_distribution<float> dist(-100.0f, 100.0f); for (int i = 0; i < num_points; ++i) { init_points(0, i) = dist(rng); // x init_points(1, i) = dist(rng); // y init_points(2, i) = dist(rng); // z } float theta = -3.14159265358 / 4; // minus to transpose/invert rotation Eigen::Matrix3f rotation; rotation = Eigen::AngleAxisf(theta, Eigen::Vector3f::UnitZ()); Eigen::Vector3f translation(10.0f, 20.0f, 30.0f); auto start_time = std::chrono::high_resolution_clock::now(); transformed_points.noalias() = rotation * init_points; transformed_points.colwise() += translation; auto end_time = std::chrono::high_resolution_clock::now(); std::chrono::duration<double, std::milli> duration_ms = end_time - start_time; std::cout << "total consume: " << duration_ms.count() << "ms" << std::endl; std::cout << "first 5 points:(x,y,z)" << std::endl; for (int i = 0; i < 5; ++i) { std::cout << "("<< transformed_points(0, i) << ","<< transformed_points(1, i) << ", " << transformed_points(2, i) << ")" << std::endl; }


© www.soinside.com 2019 - 2025. All rights reserved.