使用特征惰性求值将 Nx3 矩阵的每一行乘以不同的 3x3 旋转矩阵的最佳方法是什么?

问题描述 投票:0回答:1

和标题差不多。我正在尝试想出一种好方法来表达这一点,而不需要创建任何临时变量,这样我就可以真正利用 Eigen 的速度。我能想到的最好的就是这个公式:

本质上,我最终得到一个

3nx3n
矩阵乘以
3nxn
矩阵,最后得到一个变换后的坐标的
3nxn
矩阵。这是最有效的方法吗?我不喜欢构建这些完整矩阵的额外开销(性能和内存方面)。

有没有办法可以在一组连续的数据上使用

Eigen::Map
,例如
[a0, b0, c0, d0, e0, f0, g0, h0, i0, a1, b1, c1, d1,...]
,它的行为就像下面的稀疏矩阵吗?

此外,如果我的上述方法是最明智的方法,是否有一种有效的方法将结果矩阵折叠成结果的非稀疏

3xN
矩阵?

c++ eigen
1个回答
1
投票

这不会按您希望的方式工作。主要问题是,对于密集矩阵格式,您将大大增加操作数量 - 其中大多数为零 - 而对于稀疏矩阵乘积,特征没有块对角稀疏格式(尽管我们可以如果我们愿意的话,可以建造一个

相反,Eigen 可以很好地优化小型固定大小矩阵或较大矩阵块。使用

Matrix3f
Matrix3Xf
之类的东西比使用运行时在一维或两个维度上大小均为 3 的
MatrixXf
要好得多。

因此,一个不错的选择是这样做:

    int rotation_count = ...;
    Eigen::Matrix3Xf rotations = Eigen::Matrix3Xf::Random(3, rotation_count * 3);
    Eigen::Matrix3Xf positions = Eigen::Matrix3Xf::Random(3, rotation_count);
    Eigen::Matrix3Xf out(3, rotation_count);
#   pragma omp parallel for
    for(Eigen::Index i = 0; i < rotation_count; ++i)
        out.col(i).noalias() =
              rotations.middleCols<3>(i * 3) * positions.col(i);

特别注意两点:

  1. 行数是固定的,而不是列数是固定的。使用 Eigen 的列主矩阵格式,这意味着
    middleCols
    指的是连续的 3x3 段,而不是从一列到下一列的大步幅。
  2. 我们使用
    middleCols
    的模板重载来创建一个带有编译时信息的块,它是3x3

我们可以通过填充到 4x4 矩阵来进一步改进这一点。这会浪费一点内存,但它可以实现更好的矢量化,因为 4 个浮点数填充一个 SSE 寄存器或 4 个双精度数填充一个 AVX 寄存器。

    int rotation_count = ...;
    // With real data, keep the 4-th column and every 4-th row zero
    Eigen::Matrix4Xf rotations = Eigen::Matrix4Xf::Random(4, rotation_count * 4);
    Eigen::Matrix4Xf positions = Eigen::Matrix4Xf::Random(4, rotation_count);
    Eigen::Matrix4Xf out(4, rotation_count);
#   pragma omp parallel for
    for(Eigen::Index i = 0; i < rotation_count; ++i)
        out.col(i).noalias() =
              rotations.middleCols<4>(i * 4) * positions.col(i);

在我的系统上,3x3 实现在 3.7 秒内使用 10,000 个旋转矩阵执行 100,000 次重复,而 4x4 实现则需要 2.6 秒。确保使用

-DNDEBUG
进行编译以进行生产,以消除断言的成本。

或者,您可以尝试使用 Eigen 不受支持的张量模块来实现此目的,但我不熟悉它的用途。

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