[Eigen中文文档] 包含Eigen对象的结构体

文档总目录

英文原文(Structures Having Eigen Members)

摘要

如果定义的结构体包含固定大小的可向量化 Eigen 类型成员,则必须确保对其调用 operator new 来分配正确的对齐缓冲区。如果仅使用足够新的编译器(例如,GCC>=7、clang>=5、MSVC>=19.12)以 [c++17] 模式编译,那么编译器会自动处理所有事情,可以跳过本节。

否则,必须重载它的 operator new 以便它生成正确对齐的指针(例如,Vector4d 和 AVX 的 32 字节对齐)。幸运的是,Eigen 为提供了一个宏 EIGEN_MAKE_ALIGNED_OPERATOR_NEW 来完成这项工作。

需要修改什么样的代码?

需要更改的代码类型如下:

class Foo
{
  ...
  Eigen::Vector2d v;
  ...
};
 
...
 
Foo *foo = new Foo;

即如果有一个类,该类的成员是一个固定大小的可向量化 Eigen 对象,则需要动态创建该类的对象。

这样的代码应该如何修改?

只需要将 EIGEN_MAKE_ALIGNED_OPERATOR_NEW 宏放在类的公共部分,如下所示:

class Foo
{
  ...
  Eigen::Vector4d v;
  ...
public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};
 
...
 
Foo *foo = new Foo;

这个宏使 new Foo 返回一个对齐的指针。在 [c++17] 中,这个宏是空的,因为编译器会自动处理所有事情。

如果此方法过于麻烦,另请参阅其他解决方案,见下文。

为什么需要这样修改?

假设有如下代码:

class Foo
{
  ...
  Eigen::Vector4d v;
  ...
};
 
...
 
Foo *foo = new Foo;

一个 Eigen::Vector4d 由 4 个双精度数组成,即 256 位。这正好是 AVX 寄存器的大小,这使得可以使用 AVX 对该向量进行各种操作。但是 AVX 指令(至少是 Eigen 使用的指令速度很快)需要 256 位对齐,否则会出现段错误。

出于这个原因,Eigen 通过以下两点要求 Eigen::Vector4d 进行 256 位对齐:

  • Eigen 通过 alignas 关键字要求 Eigen::Vector4d 的数组(4 个双精度数)进行 256 位对齐。
  • Eigen 重载了 Eigen::Vector4doperator new,因此它将始终返回 256 位对齐的指针。 (在 [c++17] 中删除)

通常情况下,Eigen 会处理 operator new 的对齐,但当有一个像上面那样的 Foo 类,并且像上面那样动态分配一个新的 Foo 时,由于 Foo 没有对齐的 operator new,返回的指针 foo 不一定是 256 位对齐的。

成员 v 的对齐属性依赖于类 Foo 的属性,如果 foo 指针没有对齐,那么 foo->v 也不会对齐!通常是让类 Foo 有一个对齐的 operator new,正如我们在上一节中展示的那样。

此解释也适用于需要 16 字节对齐的 SSE/NEON/MSA/Altivec/VSX 对象,以及需要 64 字节对齐的 AVX512 固定大小对象(例如,Eigen::Matrix4d)。

是否应该把 Eigen 类型的所有成员放在类的开头?

这不是必需的。由于 Eigen 会自动处理对齐,所以像这样的代码是正常的:

class Foo
{
  double x;
  Eigen::Vector4d v;
public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};

也就是说,像往常一样,建议对成员进行排序,以便对齐不会浪费内存。在上面的示例中,对于 AVX,编译器必须在 xv 之间保留 24 个空字节。

动态大小的矩阵和向量呢?

动态大小的矩阵和向量,例如 Eigen::VectorXd,会动态分配它们自己的元素数组,因此它们会自动处理要求绝对对齐的问题,所以他们没有这个问题。这里讨论的问题仅适用于固定大小的可向量化矩阵和向量。

这是 Eigen 中的Bug吗?

不,这不是 Eigen 中的Bug,它更像是 c++ 语言规范的固有问题,已在 c++17 中通过称为 过度对齐数据的动态内存分配 功能解决。

怎样有条件地执行此操作(取决于模板参数)?

对于这种情况,我们提供宏 EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF(NeedsToAlign)。如果 NeedsToAligntrue,它将生成对齐的运算符,如 EIGEN_MAKE_ALIGNED_OPERATOR_NEW。如果 NeedsToAlign false,它将生成具有默认对齐方式的运算符。在 [c++17] 中,这个宏是空的。

示例如下:

template<int n> class Foo
{
  typedef Eigen::Matrix<float,n,1> Vector;
  enum { NeedsToAlign = (sizeof(Vector)%16)==0 };
  ...
  Vector v;
  ...
public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF(NeedsToAlign)
};
 
...
 
Foo<4> *foo4 = new Foo<4>; // foo4 is guaranteed to be 128bit-aligned
Foo<3> *foo3 = new Foo<3>; // foo3 has only the system default alignment guarantee
其他解决方案

如果随处放置 EIGEN_MAKE_ALIGNED_OPERATOR_NEW 宏过于麻烦,至少还有两个其他解决方案。

禁用对齐

第一个是禁用固定大小成员的对齐要求:

class Foo
{
  ...
  Eigen::Matrix<double,4,1,Eigen::DontAlign> v;
  ...
};

这里的 v 与对齐的 Eigen::Vector4d 完全兼容。但这样会使对 v 的加载/存储效率更低(通常略有减少,但这取决于硬件)。

私有结构体

第二个是将固定大小的对象存储到一个私有结构体中,该结构体将在主对象构造时动态分配:

struct Foo_d
{
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
  Vector4d v;
  ...
};
 
 
struct Foo {
  Foo() { init_d(); }
  ~Foo() { delete d; }
  void bar()
  {
    // use d->v instead of v
    ...
  }
private:
  void init_d() { d = new Foo_d; }
  Foo_d* d;
};

这里的明显优势是 Foo 类在对齐问题上保持不变。缺点是无论如何都需要额外的堆内存空间分配。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

万俟淋曦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值