VINS-MONO拓展2----更快地makeHessian矩阵

1. 目标

完成大作业T2
在这里插入图片描述

作业提示:
在这里插入图片描述

多线程方法主要包括以下几种(参考博客):

  • MPI(多主机多线程开发),
  • OpenMP(为单主机多线程开发而设计)
  • SSE(主要增强CPU浮点运算的能力)
  • CUDA
  • Stream processing,

之前已经了解过std::threadpthread,拓展1中makeHessian使用的是p_thread,这次正好用这几种方法与p_thread进行对比。

1. OpenMP

1.1 简单介绍

  1. 并行计算变量,共享则无需声明,如果需要该变量在每个线程中不同,则需要声明为private,如下,计算img中各个pixel的颜色,x共享,但y每个线程独立,即每个线程单独处理一行,都从第0列开始处理。
  2. 线程的分配方式是顺序分配给所有核(core),如果多于核的个数,则再从第0个核开始分配。
  3. 语法,大概为两种,一种是普通代码的多线程,另一种是循环的多线程:
//普通多线程
#pragma omp parallel private(shared_var, tid)
{related code
}//多线程循环
#pragma omp parallel for private(y, tid)//循环的代码for(){for()//二层循环等,不是必须		}//多线程结束,其它代码

1.2 快速上手

根据tutorial的实验代码:

#include <cstdio>
//#include "stdafx.h"
#include <omp.h>//需要的头文件int main(int argc, char* argv[])
{// This statement should only print onceprintf("Starting Program!\n");int nThreads, tid;int shared_var = 200000;int x,y;int width=3, height=3;
#pragma omp parallel for private(y, tid)for(x=0; x < height; x++){for(y=0; y < width; y++){tid = omp_get_thread_num();printf("thread: %d, x: %d, y: %d\n", tid, x, y);}}// We're out of the parallelized secion.// Therefor, this should execute only onceprintf("Finished!\n");return 0;
}

Cmakelists:

find_package(OpenMP REQUIRED)
add_executable(test_OpenMP OpenMP/test_OpenMP.cpp)
target_link_libraries(test_OpenMP PUBLIC OpenMP::OpenMP_CXX)

实验结果:
关于private的探究:
y为共享:
在这里插入图片描述

y为private
在这里插入图片描述

所以tutorial中的例程,意义是利用OpenMp多线程处理一张图片中的每一行,从每行的第0列开始处理:
在这里插入图片描述

for中线程的分配:
在这里插入图片描述

1.3 VINS-MONO移植

VINS-MONO中已经提供了pthread多线程和单线程makeHessian的方法,了解了OpenMP之后,我们需要使用单线程的方法,并告诉编译器使用OpenMP来进行,makeHessian代码如下:

void Solver::makeHessian()
{int pos = 0;//Hessian矩阵整体维度//it.first是要被marg掉的变量的地址,将其size累加起来就得到了所有被marg的变量的总localSize=m//marg的放一起,共m维,remain放一起,共n维for (auto &it : parameter_block_idx){it.second = pos;//也算是排序1pos += localSize(parameter_block_size[it.first]);//PQ7为改为6维}m = pos;//要被marg的变量的总维度int tmp_n = 0;//与[0]相关总维度for (const auto &it : parameter_block_size){if (parameter_block_idx.find(it.first) == parameter_block_idx.end())//将不在drop_set中的剩下的维度加起来,这一步实际上算的就是n{parameter_block_idx[it.first] = pos;//排序2tmp_n += localSize(it.second);pos += localSize(it.second);}}n = pos - m;//remain变量的总维度,这样写建立了n和m间的关系,表意更强ROS_DEBUG("\nn: %d, tmp_n: %d", n, tmp_n);ROS_DEBUG("\nSolver, pos: %d, m: %d, n: %d, size: %d", pos, m, n, (int)parameter_block_idx.size());TicToc t_summing;Eigen::MatrixXd A(pos, pos);//总系数矩阵Eigen::VectorXd b(pos);//总误差项A.setZero();b.setZero();Hessian_.resize(pos,pos);b_.resize(pos);delta_x_.resize(pos);//multi threadTicToc t_thread_summing;ROS_DEBUG("\nmulti thread: %s", MULTI_THREAD.c_str());if(MULTI_THREAD=="pthread") {pthread_t tids[NUM_THREADS];//4个线程构建//携带每个线程的输入输出信息ThreadsStruct threadsstruct[NUM_THREADS];//将先验约束因子平均分配到4个线程中int i = 0;for (auto it : factors){threadsstruct[i].sub_factors.push_back(it);i++;i = i % NUM_THREADS;}//将每个线程构建的A和b加起来for (int i = 0; i < NUM_THREADS; i++){TicToc zero_matrix;threadsstruct[i].A = Eigen::MatrixXd::Zero(pos,pos);threadsstruct[i].b = Eigen::VectorXd::Zero(pos);threadsstruct[i].parameter_block_size = parameter_block_size;//marg里的block_size,4个线程共享threadsstruct[i].parameter_block_idx = parameter_block_idx;int ret = pthread_create( &tids[i], NULL, ThreadsConstructA ,(void*)&(threadsstruct[i]));//参数4是arg,void*类型,取其地址并强制类型转换if (ret != 0){ROS_WARN("pthread_create error");ROS_BREAK();}}//将每个线程构建的A和b加起来for( int i = NUM_THREADS - 1; i >= 0; i--){pthread_join( tids[i], NULL );//阻塞等待线程完成,这里的A和b的+=操作在主线程中是阻塞的,+=的顺序是pthread_join的顺序A += threadsstruct[i].A;b += threadsstruct[i].b;}} else if(MULTI_THREAD=="openmp") {//OpenMP多线程
#pragma omp parallel forfor(size_t k = 0; k < factors.size(); ++k) { // for (auto it : factors){ResidualBlockInfo* it = factors[k];//J^T*Jfor (int i = 0; i < static_cast<int>(it->parameter_blocks.size()); i++){int idx_i = parameter_block_idx[reinterpret_cast<long>(it->parameter_blocks[i])];//要被marg的second=0int size_i = localSize(parameter_block_size[reinterpret_cast<long>(it->parameter_blocks[i])]);Eigen::MatrixXd jacobian_i = it->jacobians[i].leftCols(size_i);//remain变量的初始jacobianfor (int j = i; j < static_cast<int>(it->parameter_blocks.size()); j++){int idx_j = parameter_block_idx[reinterpret_cast<long>(it->parameter_blocks[j])];int size_j = localSize(parameter_block_size[reinterpret_cast<long>(it->parameter_blocks[j])]);Eigen::MatrixXd jacobian_j = it->jacobians[j].leftCols(size_j);//marg变量的初始jacobian//主对角线,注意这里是+=,可能之前别的变量在这个地方已经有过值了,所以要+=if (i == j)A.block(idx_i, idx_j, size_i, size_j) += jacobian_i.transpose() * jacobian_j;//非主对角线else{A.block(idx_i, idx_j, size_i, size_j) += jacobian_i.transpose() * jacobian_j;A.block(idx_j, idx_i, size_j, size_i) = A.block(idx_i, idx_j, size_i, size_j).transpose();}}b.segment(idx_i, size_i) += jacobian_i.transpose() * it->residuals;//J^T*e}
//            ROS_DEBUG("\nTotal number of threads: %d\n", omp_get_num_threads());}}//统计multi thread makeHessian时间double pure_finish_time = t_thread_summing.toc();*pure_makeHessian_time_sum_ += pure_finish_time;++(*pure_makeHessian_times_);ROS_DEBUG("\nt_thread_summing cost: %f ms, avg_pure_makeHessian_time: %f ms, pure_makeHessian_time_sum_: %f, pure_makeHessian_times_: %f",t_thread_summing.toc(), (*pure_makeHessian_time_sum_)/(*pure_makeHessian_times_), *pure_makeHessian_time_sum_, *pure_makeHessian_times_);Hessian_ = A;b_ = -b;//DOGLEG需反解出J和eif(method_==solve::Solver::kDOGLEG) {TicToc t_solve_J;TicToc t_SelfAdjoint;Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> saes2(A);//这一句24.3msROS_DEBUG("\nt_SelfAdjoint cost: %f ms", t_SelfAdjoint.toc());Eigen::VectorXd S = Eigen::VectorXd((saes2.eigenvalues().array() > eps).select(saes2.eigenvalues().array(), 0));Eigen::VectorXd S_sqrt = S.cwiseSqrt();//开根号linearized_jacobians = S_sqrt.asDiagonal() * saes2.eigenvectors().transpose();Eigen::VectorXd S_inv = Eigen::VectorXd((saes2.eigenvalues().array() > eps).select(saes2.eigenvalues().array().inverse(), 0));Eigen::VectorXd S_inv_sqrt = S_inv.cwiseSqrt();linearized_residuals = S_inv_sqrt.asDiagonal() * saes2.eigenvectors().real().transpose() * b;ROS_DEBUG("\nt_solve_J cost: %f ms", t_solve_J.toc());//25ms}
}

其中注意for循环的写法,OpenMP似乎只支持for(int i=0; i<10; ++i)这种类型的循环,使用
for(auto i t :factors)这种写法则编译不过。

实验发现,openmp makeHessian的精度可能比pthread差,查看原因是 ρ \rho ρ经常 < 0 <0 <0,怀疑Hessian精度问题。makeHessian的时间跟Hessian的稠密程度也有关,发散时的makeHessian速度也很快,因为非常稀疏。所以对makeHessian速度的对比需要在收敛的情况下进行。

1.4 pthread与OpenMP对比

对比实验结果如下,倾向于使用pthread:
在这里插入图片描述

2. SSE

SSE 的指令集是 X86 架构 CPU 特有的,对于 ARM 架构、MIPS 架构等 CPU
是不支持的,所以使用了 SSE 指令集的程序,是不具备可移植标准的。而移动机器人平台
使用的 CPU 大多为了保证低功耗采用了 ARM 架构,所以这样的加速在移动机器人平台上
会失效。

大概看了下SSE的用法,如果需要用SSE,可能需要大改VINS0-MONO中的数据结构,有些不划算,暂不考虑。

3. CUDA

参考文章

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/325931.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024年阿里云优惠活动清单_优惠代金券领取大全

阿里云服务器优惠活动大全包括&#xff1a;云服务器新人特惠、云小站、阿里云免费中心、学生主机优惠、云服务器精选特惠、阿里云领券中心等&#xff0c;活动上阿里云服务器ECS经济型e实例2核2G、3M固定带宽99元一年、轻量应用服务器2核2G3M带宽轻量服务器一年61元&#xff0c;…

私有云平台搭建openstack和ceph结合搭建手册

OpenStack与云计算 什么是云&#xff1f; 如何正确理解云&#xff0c;可以从以下几个方面。 云的构成。 用户&#xff1a;对用户而言是透明无感知的&#xff0c;不用关心底层构成&#xff0c;只需要知道利用云完成自己任务即可。 云提供商&#xff1a;对云资产管理和运维。 云…

详解卡尔曼滤波(Kalman Filter)

1. 从维纳滤波到卡尔曼滤波 黑盒&#xff08;Black Box&#xff09;思想最早由维纳&#xff08;Wiener&#xff09;在1939年提出&#xff0c;即假定我们对从数据到估计中间的映射过程一无所知&#xff0c;仅仅用线性估计&#xff08;我们知道在高斯背景下&#xff0c;线性估计…

Unity组件开发--升降梯

我开发的升降梯由三个部分组成&#xff0c;反正适用于我的需求了&#xff0c;其他人想复用到自己的项目的话&#xff0c;不一定。写的也不是很好&#xff0c;感觉搞的有点复杂啦。完全可以在优化一下&#xff0c;项目赶工期&#xff0c;就先这样吧。能用就行&#xff0c;其他的…

异常“Service id not legal hostname (sc_goods)”处理

Service id not legal hostname (sc_goods)异常处理 遇到问题&#xff1a; 写Feign调微服务时&#xff0c;发现该服务的服务名sc_goods”的格式&#xff0c;于是Feign的FeignClient 注解写成了 FeignClient(value "sc_goods") 然后启动服务&#xff0c;直接报错…

【年终总结系列 2023】新起点,同时追寻更高的起点

什么是攀登者&#xff0c;用一个场景来概括就是&#xff1a;经常弯腰低头手脚并用向上攀爬&#xff0c;待到山的顶峰后终于可以舒展一下身体&#xff0c;但若舒展的时间过长便会觉得无聊&#xff0c;此时向远处眺望&#xff0c;发现了更高的山峰&#xff0c;便又充满了激情。对…

C#,快速排序算法(Quick Sort)的非递归实现与数据可视化

排序算法是编程的基础。 常见的四种排序算法是&#xff1a;简单选择排序、冒泡排序、插入排序和快速排序。其中的快速排序的优势明显&#xff0c;一般使用递归方式实现&#xff0c;但遇到数据量大的情况则无法适用。实际工程中一般使用“非递归”方式实现。 快速排序(Quick Sor…

iOS苹果和Android安卓测试APP应用程序的区别差异

在移动应用开发中&#xff0c;测试是一个至关重要的环节。无论是iOS苹果还是Android安卓&#xff0c;测试APP应用程序都需要注意一些差异和细节。本文将详细介绍iOS和Android的测试差异&#xff0c;包括操作系统版本、设备适配、测试工具和测试策略&#xff0c;并回答一些新手容…

Flutter+Go_Router+Fluent_Ui仿阿里网盘桌面软件开发跨平台实战-买就送仿小米app开发

Flutter是谷歌公司开发的一款开源、免费的UI框架&#xff0c;可以让我们快速的在Android和iOS上构建高质量App。它最大的特点就是跨平台、以及高性能。 目前 Flutter 已经支持 iOS、Android、Web、Windows、macOS、Linux 的跨平台开发。 Flutter官方介绍&#xff0c;目前Flutte…

CentOS 8 8.5.2111 网络在线安装系统 —— 筑梦之路

之前写过一篇关于centos 8 官方停止更新维护后解决yum源问题的文章&#xff1a; CentOS 8 停止维护后换可用yum源——筑梦之路_http://ftp.iij.ad.jp/pub/linux/centos-vault/8.5.21-CSDN博客 由于centos 8 dvd的镜像比较大&#xff0c;有时候我们根本不需要去下载一个10G以上…

纵向拼接图片,一键批量处理,工作更高效!

在创意和设计工作中&#xff0c;图片的拼接和处理是必不可少的环节。为了帮助您更高效地完成这一任务&#xff0c;我们推出了一款纵向拼接图片的批量处理工具。这款工具不仅操作简单&#xff0c;而且功能强大&#xff0c;让您的图片处理工作变得轻松又高效&#xff01; 首先&a…

leetcode 每日一题 2024年01月06日 在链表中插入最大公约数

题目 2807. 在链表中插入最大公约数 给你一个链表的头 head &#xff0c;每个结点包含一个整数值。 在相邻结点之间&#xff0c;请你插入一个新的结点&#xff0c;结点值为这两个相邻结点值的 最大公约数 。 请你返回插入之后的链表。 两个数的 最大公约数 是可以被两个数…