目录
1 缘起
2 什么是向量化运算?
2.1 A Simple Example
2.2 重构
2.3 Heap vs Stack
2.4 还有坑?
3 再谈Eigen
4 总结
1 缘起Eigen是一个非常常用的矩阵运算库,至少对于SLAM的研究者来说不可或缺。然而,向来乖巧的Eigen近来却频频闹脾气,把我的程序折腾得死去活来,我却是丈二和尚摸不着头脑。
简单说说我经历的灵异事件。我的程序原本在NVIDIA TX2上跑的好好的,直到有一天,我打算把它放到服务器上,看看传说中的RTX 2080GPU能不能加速一把。结果悲剧发生了,编译正常,但是一运行就立即double free。我很是吃惊,怎么能一行代码都没执行就崩了呢。但崩了就是崩了,一定是哪里有bug,我用valgrind检查内存问题,发现种种线索都指向g2o。g2o是一个SLAM后端优化库,里面封装了大量SLAM相关的优化算法,内部使用了Eigen进行矩阵运算。阴差阳错之间,我发现关闭-march=native这个编译选项后就能正常运行,而这个编译选项其实是告诉编译器当前的处理器支持哪些SIMD指令集,Eigen中又恰好使用了SSE、AVX等指令集进行向量化加速。此时,机智的我发现Eigen文档中有一章叫做Alignment issues,里面提到了某些情况下Eigen对象可能没有内存对齐,从而导致程序崩溃。现在,证据到齐,基本可以确定我遇到的真实问题了:编译安装g2o时,默认没有使用-march=native,因此里面的Eigen代码没有使用向量化加速,所以它们并没有内存对齐。而在我的程序中,启用了向量化加速,所有的Eigen对象都是内存对齐的。两个程序链接起来之后,g2o中未对齐的Eigen对象一旦传递到我的代码中,向量化运算的指令就会触发异常。解决方案很简单,要么都用-march=native,要么都不用。
这件事就这么过去了,但我不能轻易放过它,毕竟花费了那么多时间找bug。后来我又做了一些深入的探究,这篇文章就来谈谈向量化和内存对齐里面的门道。
2 什么是向量化运算?向量化运算就是用SSE、AVX等SIMD(Single Instruction Multiple Data)指令集,实现一条指令对多个操作数的运算,从而提高代码的吞吐量,实现加速效果。SSE是一个系列,包括从最初的SSE到最新的SSE4.2,支持同时操作16 bytes的数据,即4个float或者2个double。AVX也是一个系列,它是SSE的升级版,支持同时操作32 bytes的数据,即8个float或者4个double。
但向量化运算是有前提的,那就是内存对齐。SSE的操作数,必须16 bytes对齐,而AVX的操作数,必须32 bytes对齐。也就是说,如果我们有4个float数,必须把它们放在连续的且首地址为16的倍数的内存空间中,才能调用SSE的指令进行运算。
2.1 A Simple Example为了给没接触过向量化编程的同学一些直观的感受,我写了一个简单的示例程序:
// gcc编译支持AVX2指令的编程。程序中需要使用头文件和,
// 这样通过调用其中定义的一些函数,达到使用AVX2指令的目的,
// 即用C/C++调用SIMD指令(单指令多数据)。
#include
#include
// 同时计算4对double的和
int main() {
double input1[4] = {1, 1, 1, 1};
double input2[4] = {1, 2, 3, 4};
double result[4];
std::cout
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?