Eigen::Vector3f 对齐

Posted

技术标签:

【中文标题】Eigen::Vector3f 对齐【英文标题】:Eigen::Vector3f alignment 【发布时间】:2014-10-04 11:51:22 【问题描述】:

我正在使用 Eigen 处理非结构化点集(点云),表示为 Eigen::Vector3f 对象的数组。为了启用 SIMD 矢量化,我将Vector3f 子类化为一个带有 alignas(16) 的类。数组中的每个对象都以 16 字节的边界开始,彼此之间有 4 字节的间隙,并且包含未初始化的数据。

子类目前看起来像这样:(仍然需要添加模板复制构造函数和 operator=,如 Eigen 文档中所示)

struct alignas(16) point_xyz : public Eigen::Vector3f 
    using Eigen::Vector3f::Vector3f;
;

point_xyz cloud[n];

程序集输出显示正在使用 SIMD 指令,并且对数组中的每个 point_xyz 应用转换的程序似乎工作正常。

以这种方式使用 Eigen 是否安全,或者结果是否取决于未使用的 4 字节间隙的内容等?

另外,将 RGB 颜色数据或其他数据放入未使用的 4 个字节是否安全(需要覆盖内存对齐)?

编辑: 启用优化时,clang++ 和 g++ 似乎都进行了一些矢量化。 如果没有优化(以及低于 -O2 的 clang++),两者都会生成对 Eigen 库函数的调用以进行以下矩阵乘法(转换):

using transform_t = Eigen::Transform<float, 3, Eigen::Affine>;
transform_t t = Eigen::AngleAxisf(0.01*M_PI, Eigen::Vector3f::UnitX()) * Eigen::Translation3f(0.1, 0.1, 0.1);
Eigen::Vector3f p(123, 234, 345);
std::cout << p << std::endl;

for(;;) 
  asm("# BEGIN TRANS");
  p = t * p;
  asm("# END TRANS");

std::cout << p << std::endl;

(需要 for 循环和 cout,以便优化不会删除乘法或放入常量值)。

在 GCC (-O1) 中会导致

# 50 "src/main.cc" 1
    # BEGIN TRANS
# 0 "" 2
    movss   (%rsp), %xmm4
    movaps  %xmm4, %xmm2
    mulss   64(%rsp), %xmm2
    movss   4(%rsp), %xmm0
    movaps  %xmm0, %xmm1
    mulss   80(%rsp), %xmm1
    addss   %xmm1, %xmm2
    movss   8(%rsp), %xmm3
    movaps  %xmm4, %xmm5
    mulss   68(%rsp), %xmm5
    movaps  %xmm0, %xmm1
    mulss   84(%rsp), %xmm1
    addss   %xmm5, %xmm1
    movaps  %xmm3, %xmm5
    mulss   100(%rsp), %xmm5
    addss   %xmm5, %xmm1
    addss   116(%rsp), %xmm1
    mulss   72(%rsp), %xmm4
    mulss   88(%rsp), %xmm0
    addss   %xmm4, %xmm0
    movaps  %xmm3, %xmm4
    mulss   104(%rsp), %xmm4
    addss   %xmm4, %xmm0
    addss   120(%rsp), %xmm0
    mulss   96(%rsp), %xmm3
    addss   %xmm3, %xmm2
    addss   112(%rsp), %xmm2
    movss   %xmm2, (%rsp)
    movss   %xmm1, 4(%rsp)
    movss   %xmm0, 8(%rsp)
# 52 "src/main.cc" 1
    # END TRANS
# 0 "" 2

无论有没有#define EIGEN_DONT_VECTORIZE 1,它都会产生相同的输出。使用Vector4f,当 Eigen 的矢量化未禁用但两者都在 xmm 寄存器上运行时,会生成稍短的输出。

AlignedVector3&lt;float&gt; 似乎不支持与Eigen::Transform 的乘法。我正在对使用 3 个(非同质)坐标表示的点集进行仿射变换。我不确定 Eigen 如何使用 Eigen::Transform&lt;float, 3, Eigen::Affine&gt;Eigen::Vector4f 向量实现转换。 IE。它是否只改变向量的前 3 个分量,第四个分量是否必须为零,或者它可以包含任意值,还是将 4 向量解释为齐次坐标?它是否取决于转换的内部表示(AffineAffineCompactProjective)。

【问题讨论】:

我很惊讶 SIMD 正在被使用。可以肯定的是,在这种情况下,Eigen 不会显式生成 SIMD。检查 unsupported/Eigen/AlignedVector3 模块以获取显式矢量化 vector3f。 代码似乎效率低下。当然会使用“SIMD”,因为大多数编译器默认使用 SSE 进行 fp 操作(即不再容易生成 80x87 代码)。 mulss = mul 标量单精度。它使用向量寄存器,但它不是 SIMD。这就是 amd64 做数学的方式; x87 对于除 80 位 long double 之外的所有内容都已过时(这比任何其他获得超过 64 位浮点数的方式都快得多)。 【参考方案1】:

不要这样做! 请改用Aligned Vector3 unsupported module。 例如:

#include <unsupported/Eigen/AlignedVector3>
// ...
// and use it somewhere in the code:
Eigen::AlignedVector3<double> a, b;
// ...
a += b;// will use simd instruction, even known they aren't real 3d vectors.

这个类的工作方式是在不使用最新系数时在内部存储一个 4d 向量(按 Eigen 规则对齐)。

这个类使它对用户透明。像使用普通 3d 矢量一样使用它,就这么简单!


问题中的 asm 没有使用 SIMD,只是像 x86-64 一样在向量寄存器中进行标量 FP 操作。 (当 SSE 可用时,对于 x86-32)。 addss 是一个 FP 添加标量单精度。 (addps 是加装单品)。

仅在 gcc 中 -O3 启用了自动矢量化,而不是 -O2,而且这只使用了 -O1

每个 16B 块中的未初始化元素主要阻止 SIMD 浮点,因为它们可能包含使数学指令减慢一个数量级以上的非正规或 NaN(除非您启用非正规为零和刷新为零,并且编译器知道这一点并可以利用它)。整数指令在某些元素中没有垃圾的性能问题这一弱点,但不要指望编译器会自动向量化。

【讨论】:

虽然这可能会回答问题,但it would be preferable 在此处包含答案的基本部分,并提供链接以供参考。

以上是关于Eigen::Vector3f 对齐的主要内容,如果未能解决你的问题,请参考以下文章

BoxGeometry 未与 SphereGeometry 正确对齐

对类使用 C 内在函数和内存对齐困难

XNA Vector3 转换为 Vector3D

Unity3D之Vector3.Dot和Vector3.Cross的使用

Unity中xLua与toLua对Vector3的优化

Unity3D之Vector3.Dot和Vector3.Cross的使用