将 Eigen 类型与 STL 容器和 std::vector 结合使用
Using Eigen types with STL containers and std::vector
看起来在STL容器中使用Eigen类型非常混乱,需要特别注意对齐问题。我的问题是,我打算创建复杂的 class 层次结构,其中包含许多 classes,其中可能包含一种或多种 Eigen 类型作为成员变量。从文档中可以看出,一旦您在成员变量中包含 Eigen 类型,您的 class 就会得到 "infected",并出现与 Eigen 类型相同的问题。这意味着我必须格外小心地使用 STL 容器,不仅用于 Eigen 类型,而且用于我所有的几十个 classes。
更让我担心的是,任何在他们的代码中使用我的 classes 实例的人都会遇到同样的问题,并且需要成为这方面的专家 - 即使我的 classes 没有在其 public 接口中公开任何 Eigen 类型!
这真令人沮丧。我有问题,
- 我上面的理解是否正确(我只需要支持C++11和现代编译器)?
- 是否有人们使用的任何模式,这样他们就不必到处用特殊的 Eigen 处理来污染他们的代码?
- 我正在考虑全局禁用整个矢量化。这会以性能为代价解决上述问题吗?是否可以仅针对特定代码有选择地启用它?
- 如果我忘记处理代码中某处的对齐问题,我是否总是会遇到编译时错误,或者问题可能仍然隐藏并且在运行时可能会崩溃?
是的,您的理解大部分是正确的,但我要补充一点,这仅涉及需要对齐的 Eigen 固定大小类型,例如 Vector4f
、Matrix2d
等,但不涉及 Vector3f
或 MatrixXd
。此外,问题的核心是 STL 容器还不符合 alignas
要求,尽管这应该会出现在未来的某个 C++ 版本中。
我认为避免此类困难的最简单方法是对 class 成员和容器值类型使用非对齐的 Eigen 类型,例如:
typedef Eigen::Matrix<float,4,1,Eigen::DontAlign> UVector4f;
typedef Eigen::Matrix<double,2,2,Eigen::DontAlign> UMatrix2d;
这样您就不必担心对齐问题,也不会丢失显式矢量化。在 Eigen 3.3 中,未对齐的对象也被矢量化。
编辑:
关于你的最后一个问题,不幸的是,在 C++ 中不可能在编译时检测到这样的缺点。如果未禁用断言并且在 运行 时间发生无效的未对齐分配,那么您将收到一条明确的断言消息,但这就是我们所能做的。因此,如果您的程序 运行 在具有某些给定编译标志的给定系统上运行良好,那么这并不意味着您的代码是安全的。例如,在大多数 64 位系统上,缓冲区在 16 字节边界上对齐,因此,如果您不启用 AVX 指令集,那么您的代码将 运行 正常。另一方面,如果移动到更奇特的平台,或者启用默认需要 32 字节对齐的 AVX 指令,则相同的代码可能会断言。尽管如此,静态分析器正变得越来越强大,我认为其中一些问题可以被它们检测到。
另一种策略是使用自定义 malloc
检查您的程序,仅返回 8 字节对齐的缓冲区。假设您的程序已被单元测试很好地覆盖,那么您应该能够发现所有缺点。为此,出于显而易见的原因,您必须使用 -DEIGEN_MALLOC_ALREADY_ALIGNED=0
进行编译。
看起来在STL容器中使用Eigen类型非常混乱,需要特别注意对齐问题。我的问题是,我打算创建复杂的 class 层次结构,其中包含许多 classes,其中可能包含一种或多种 Eigen 类型作为成员变量。从文档中可以看出,一旦您在成员变量中包含 Eigen 类型,您的 class 就会得到 "infected",并出现与 Eigen 类型相同的问题。这意味着我必须格外小心地使用 STL 容器,不仅用于 Eigen 类型,而且用于我所有的几十个 classes。
更让我担心的是,任何在他们的代码中使用我的 classes 实例的人都会遇到同样的问题,并且需要成为这方面的专家 - 即使我的 classes 没有在其 public 接口中公开任何 Eigen 类型!
这真令人沮丧。我有问题,
- 我上面的理解是否正确(我只需要支持C++11和现代编译器)?
- 是否有人们使用的任何模式,这样他们就不必到处用特殊的 Eigen 处理来污染他们的代码?
- 我正在考虑全局禁用整个矢量化。这会以性能为代价解决上述问题吗?是否可以仅针对特定代码有选择地启用它?
- 如果我忘记处理代码中某处的对齐问题,我是否总是会遇到编译时错误,或者问题可能仍然隐藏并且在运行时可能会崩溃?
是的,您的理解大部分是正确的,但我要补充一点,这仅涉及需要对齐的 Eigen 固定大小类型,例如 Vector4f
、Matrix2d
等,但不涉及 Vector3f
或 MatrixXd
。此外,问题的核心是 STL 容器还不符合 alignas
要求,尽管这应该会出现在未来的某个 C++ 版本中。
我认为避免此类困难的最简单方法是对 class 成员和容器值类型使用非对齐的 Eigen 类型,例如:
typedef Eigen::Matrix<float,4,1,Eigen::DontAlign> UVector4f;
typedef Eigen::Matrix<double,2,2,Eigen::DontAlign> UMatrix2d;
这样您就不必担心对齐问题,也不会丢失显式矢量化。在 Eigen 3.3 中,未对齐的对象也被矢量化。
编辑:
关于你的最后一个问题,不幸的是,在 C++ 中不可能在编译时检测到这样的缺点。如果未禁用断言并且在 运行 时间发生无效的未对齐分配,那么您将收到一条明确的断言消息,但这就是我们所能做的。因此,如果您的程序 运行 在具有某些给定编译标志的给定系统上运行良好,那么这并不意味着您的代码是安全的。例如,在大多数 64 位系统上,缓冲区在 16 字节边界上对齐,因此,如果您不启用 AVX 指令集,那么您的代码将 运行 正常。另一方面,如果移动到更奇特的平台,或者启用默认需要 32 字节对齐的 AVX 指令,则相同的代码可能会断言。尽管如此,静态分析器正变得越来越强大,我认为其中一些问题可以被它们检测到。
另一种策略是使用自定义 malloc
检查您的程序,仅返回 8 字节对齐的缓冲区。假设您的程序已被单元测试很好地覆盖,那么您应该能够发现所有缺点。为此,出于显而易见的原因,您必须使用 -DEIGEN_MALLOC_ALREADY_ALIGNED=0
进行编译。