Eigen, 向量化运算和内存对齐

If you're compiling in [c++17] mode only with a sufficiently recent compiler (e.g., GCC>=7, clang>=5, MSVC>=19.12), then everything is taken care by the compiler and you can stop reading.

如果你仅使用最新的编译器(例如,GCC>=7,clang>=5,MSVC>=19.12)在[c++17]模式下进行编译,那么编译器会处理所有问题,你可以停止阅读。

最近将算法部署到Linux上时,遇到Eigen内存对齐的问题,搜索了相关文章,做个简单的总结。

直接说结论:

1. 程序出了什么问题?

    -- 由于Eigen内存对齐问题而导致程序崩溃。具体来说,在运行过程中,向量化运算按内存对齐的方式取Eigen对象的内存地址,而我们的程序中的那些Eigen对象并不是内存对齐的。

2. 程序为什么会出现这些问题?

    --因为程序开启了向量化。向量化是一种加速运算方案,是计算机架构者提出,由C++编译器、处理器等支持实现。

    --Eigen中有些对象(Fixed-size vectorizable Eigen objects,这些对象是静态分配,且字节size是16字节的倍数)刚好满足向量化运算的条件,而C++工程又在编译选项中开启了向量化,因此程序运行时实施了向量化。

    --之所以有时候在Debug模式下程序运行正常,在Release则出现由于Eigen内存对齐导致的core dump,是因为Debug模式下没有开启向量化,而在Release模式下开启了向量化。

3. 为了避免或解决这些问题,Eigen做了什么?程序员还需要做什么?

    --有两种解决方案,要么禁用向量化,从而即使Eigen中的Fixed-size vectorizable Eigen objects不是内存对齐的程序也不会出错;要么全面支持向量化。

    --第一种方案除了会降低运算速度外,最重要的一点存在潜在风险,因为即使我们的程序中没有开启向量化,第三方库也可能开启向量化,这样同样会导致程序运行时崩溃。

    --CMakeList开启向量化的编译指令是 -march=native。

    --为了全面支持向量化,我们需要让C++程序中的Fixed-size vectorizable Eigen objects满足在声明时内存对齐,且在动态分配时也满足内存对齐。

    --直接声明的对象是存储在栈(Stack)上的,其内存地址由编译器在编译时确定。可以通过预编译指令确保内存对齐。这一点由Eigen库予以保证。

    --在动态分配时,对象的内存存在于堆(Heap)上,在运行时分配(动态分配)。C++的运行时库并不会关心预编译指令声明的对齐方式。一个有效的实践是重载对象的new运算符,new运算符会在堆上申请内存并调用对象的构造函数。可以在new 运输符申请内存时,通过特殊操作实现内存的对齐。幸运的时,Eigen库以宏的形式提供了new运算符EIGEN_MAKE_ALIGNED_OPERATOR_NEW,程序员只需要将这个宏放在C++类public的任何区域。

   --对于element为Fixed-size vectorizable Eigen objects的STL 容器,Eigen提供了用于内存对齐的函数,确保这些容器中的Eigen对象的内存满足内存对齐要求。

例如:

std::vector<Eigen::Vector4d>
std::map<int, Eigen::Vector4d>
std::unordered_map<int, Eigen::Vector4d>

应该按下面的方式写:

#include <Eigen/StdVector>
std::vector<Eigen::Vector4d,Eigen::aligned_allocator<Eigen::Vector4d> >
std::map<int, Eigen::Vector4d, Eigen::aligned_allocator<std::pair<const int, Eigen::Vector4d> > >

template<class Key, class T> 
using aligned_unordered_map = std::unordered_map<Key, T, std::hash<Key>, std::equal_to<Key>, Eigen::aligned_allocator< std::pair<const Key, T> > >;

aligned_unordered_map<int, Eigen::Vector4d>

   -- 还应该注意什么:Eigen对象传参时,应该传引用,而不应该传值,否则仍有可能引发内存对齐问题。

4. CMakeLists开启向量化的方法

find_package(Eigen3 3.3 REQUIRED CONFIG)

include(CheckCXXCompilerFlag)

check_cxx_compiler_flag("-march=native" _march_native_works)
check_cxx_compiler_flag("-xHost" _xhost_works)

set(_CXX_FLAGS)
if(_march_native_works)
  message(STATUS "Using processor's vector instructions (-march=native compiler flag set)")
  set(_CXX_FLAGS "-march=native")
elseif(_xhost_works)
  message(STATUS "Using processor's vector instructions (-xHost compiler flag set)")
  set(_CXX_FLAGS "-xHost")
else()
  message(STATUS "No suitable compiler flag found for vectorization")
endif()

add_executable(linear-algebra linear-algebra.cpp)

target_compile_options(linear-algebra
  PRIVATE
  ${_CXX_FLAGS}
)

target_link_libraries(linear-algebra
  PRIVATE
  Eigen3::Eigen
)

参考:

[1] Eigen: Alignment issues​​​​

     Eigen: Fixed-size vectorizable Eigen objects

     Eigen: Structures Having Eigen Members

     Eigen: Using STL Containers with Eigen

     Eigen: Passing Eigen objects by value to functions

[2] 从Eigen向量化谈内存对齐 - 知乎 (zhihu.com)​​​​​​

[3] 编译优化之 - 向量化优化入门_zhugl0的博客-CSDN博客_编译器函数向量化

[4] 第2章 检测环境 - 2.6 为Eigen库使能向量化 - 《CMake菜谱(CMake Cookbook中文版)》 - 书栈网 · BookStack

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Jacky_546287052

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

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

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

打赏作者

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

抵扣说明:

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

余额充值