和标题差不多。我正在尝试想出一种好方法来表达这一点,而不需要创建任何临时变量,这样我就可以真正利用 Eigen 的速度。我能想到的最好的就是这个公式:
本质上,我最终得到一个
3nx3n
矩阵乘以 3nxn
矩阵,最后得到一个变换后的坐标的 3nxn
矩阵。这是最有效的方法吗?我不喜欢构建这些完整矩阵的额外开销(性能和内存方面)。
有没有办法可以在一组连续的数据上使用
Eigen::Map
,例如[a0, b0, c0, d0, e0, f0, g0, h0, i0, a1, b1, c1, d1,...]
,它的行为就像下面的稀疏矩阵吗?
此外,如果我的上述方法是最明智的方法,是否有一种有效的方法将结果矩阵折叠成结果的非稀疏
3xN
矩阵?
这不会按您希望的方式工作。主要问题是,对于密集矩阵格式,您将大大增加操作数量 - 其中大多数为零 - 而对于稀疏矩阵乘积,特征没有块对角稀疏格式(尽管我们可以如果我们愿意的话,可以建造一个。
相反,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);
特别注意两点:
middleCols
指的是连续的 3x3 段,而不是从一列到下一列的大步幅。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 不受支持的张量模块来实现此目的,但我不熟悉它的用途。