ROS2学习笔记27--实现自定义内存分配器
概要:这篇内容主要介绍如何实现自定义内存分配器
环境:ubuntu20.04,ros2-foxy,vscode
最后如果没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常复现
4.3实现自定义内存分配器(原文:https://docs.ros.org/en/foxy/Tutorials/Allocator-Template-Tutorial.html
)
>>
教程>>
实现自定义内存分配器
你正阅读的是ros2
较老版本(Foxy
),但仍然支持的说明文档.想查看最新版本的信息,请看galactic版本链接( https://docs.ros.org/en/galactic/Tutorials.html
)
实现自定义内存分配器
目录
1.背景
2.编写一个分配器
3.编写一个main示例
4.将分配器传递给进程内部管道
5.测试和验证代码
6.TLSF分配器
本教程将教你如何为发布器和侦听器集成自定义分配器,以便在执行ROS
节点时永远不会调用默认堆分配器。本教程的代码在这里(https://github.com/ros2/demos/blob/foxy/demo_nodes_cpp/src/topics/allocator_tutorial.cpp
)。
1.背景
假设您想要编写实时安全的代码,并且你已经听说了在实时临界区调用“new”
的许多危险,因为大多数平台上的默认堆分配器是不确定性的。
默认情况下,许多c++
标准库结构会在增长时隐式分配内存,比如std::vector
。然而,这些数据结构也接受一个“Allocator”
模板参数。如果你为这些数据结构之一指定一个自定义分配器,它将为你使用该分配器而不是系统分配器来增长或收缩数据结构。你的自定义分配器可以在堆栈上预分配内存池,这可能更适合于实时应用程序。
在ROS2 C++
客户端库(rclcpp
)中,我们遵循与c++
标准库类似的理念。发布器、侦听器和Executor
接受一个Allocator
模板参数,该参数控制该实体在执行期间进行的分配。
2.编写一个分配器
要编写一个与ROS2
的分配器接口兼容的分配器,你的分配器必须与c++
标准库分配器接口兼容。
c++ 11
库提供了一个名为allocator_traits
的东西。c++ 11
标准规定,自定义分配器只需要满足以标准方式分配和释放内存所需的最小需求集。allocator_traits
是一个通用结构,它基于用最小需求编写的分配器来填充分配器的其他特性。
例如,下面的自定义分配器声明将满足allocator_traits
(当然,你仍然需要在这个结构中实现声明的函数):
template <class T>
struct custom_allocator {using value_type = T;custom_allocator() noexcept;template <class U> custom_allocator (const custom_allocator<U>&) noexcept;T* allocate (std::size_t n);void deallocate (T* p, std::size_t n);
};template <class T, class U>
constexpr bool operator== (const custom_allocator<T>&, const custom_allocator<U>&) noexcept;template <class T, class U>
constexpr bool operator!= (const custom_allocator<T>&, const custom_allocator<U>&) noexcept;
然后你可以像这样访问由allocator_traits
填充的其他函数和分配器成员:
std::allocator_traits<custom_allocator<T>>::construct(...)
要了解allocator_traits
的全部功能,请参见https://en.cppreference.com/w/cpp/memory/allocator_traits
。
然而,一些只支持部分c++ 11
的编译器,如GCC 4.8
,仍然需要分配器来实现大量的样本代码,以处理标准库结构(如vector
和string
),因为这些结构在内部不使用allocator_traits
。因此,如果你正在使用一个部分支持c++ 11
的编译器,你的分配器将需要看起来更像这样:
template<typename T>
struct pointer_traits {using reference = T &;using const_reference = const T &;
};// Avoid declaring a reference to void with an empty specialization
template<>
struct pointer_traits<void> {};template<typename T = void>
struct MyAllocator : public pointer_traits<T> {public:using value_type = T;using size_type = std::size_t;using pointer = T *;using const_pointer = const T *;using difference_type = typename std::pointer_traits<pointer>::difference_type;MyAllocator() noexcept;~MyAllocator() noexcept;template<typename U>MyAllocator(const MyAllocator<U> &) noexcept;T * allocate(size_t size, const void * = 0);void deallocate(T * ptr, size_t size);template<typename U>struct rebind {typedef MyAllocator<U> other;};
};template<typename T, typename U>
constexpr bool operator==(const MyAllocator<T> &,const MyAllocator<U> &) noexcept;template<typename T, typename U>
constexpr bool operator!=(const MyAllocator<T> &,const MyAllocator<U> &) noexcept;
3.编写一个main示例
编写了有效的c++
分配器后,必须将其作为共享指针传递给发布器、侦听器和执行程序。
auto alloc = std::make_shared<MyAllocator<void>>();
auto publisher = node->create_publisher<std_msgs::msg::UInt32>("allocator_example", 10, alloc);
auto msg_mem_strat =std::make_shared<rclcpp::message_memory_strategy::MessageMemoryStrategy<std_msgs::msg::UInt32,MyAllocator<>>>(alloc);
auto subscriber = node->create_subscription<std_msgs::msg::UInt32>("allocator_example", 10, callback, nullptr, false, msg_mem_strat, alloc);std::shared_ptr<rclcpp::memory_strategy::MemoryStrategy> memory_strategy =std::make_shared<AllocatorMemoryStrategy<MyAllocator<>>>(alloc);
rclcpp::executors::SingleThreadedExecutor executor(memory_strategy);
你还需要使用分配器分配沿着执行代码路径传递的任何消息。
auto alloc = std::make_shared<MyAllocator<void>>();
一旦你实例化了节点并将执行器添加到该节点,就该开始旋转了:
uint32_t i = 0;
while (rclcpp::ok()) {msg->data = i;i++;publisher->publish(msg);rclcpp::utilities::sleep_for(std::chrono::milliseconds(1));executor.spin_some();
}
4.将分配器传递给进程内部管道
即使我们在同一个进程中实例化了发布器和侦听器,我们还没有使用进程内通道。
IntraProcessManager
是一个通常对用户隐藏的类,但是为了将自定义分配器传递给它,我们需要通过从rclcpp
环境获取它,来公开它。IntraProcessManager
使用了几个标准库结构,因此如果没有自定义分配器,它将调用默认新的。
auto context = rclcpp::contexts::default_context::get_global_default_context();
auto ipm_state =std::make_shared<rclcpp::intra_process_manager::IntraProcessManagerState<MyAllocator<>>>();
// Constructs the intra-process manager with a custom allocator.
context->get_sub_context<rclcpp::intra_process_manager::IntraProcessManager>(ipm_state);
auto node = rclcpp::Node::make_shared("allocator_example", true);
确保在以这种方式构造节点后,实例化发布器和侦听器。
5.测试和验证代码
你如何知道,你的自定义分配器实际上正在被调用?
显而易见的做法是计算对自定义分配器的allocate
和deallocate
函数的调用(次数),并将其与对new
和delete
的调用(次数)进行比较。
向自定义分配器添加计数(功能是)很简单:
T * allocate(size_t size, const void * = 0) {// ...num_allocs++;// ...
}void deallocate(T * ptr, size_t size) {// ...num_deallocs++;// ...
}
你也可以覆盖全局new
和delete
操作符:
void operator delete(void * ptr) noexcept {if (ptr != nullptr) {if (is_running) {global_runtime_deallocs++;}std::free(ptr);ptr = nullptr;}
}void operator delete(void * ptr, size_t) noexcept {if (ptr != nullptr) {if (is_running) {global_runtime_deallocs++;}std::free(ptr);ptr = nullptr;}
}
其中,我们递增的变量只是全局静态整数,而is_running
是一个全局静态布尔值,在调用spin
之前被切换。
示例(https://github.com/ros2/demos/blob/foxy/demo_nodes_cpp/src/topics/allocator_tutorial.cpp
)可执行文件打印变量的值。要运行示例可执行文件,请使用:
allocator_example
或者,使用进程内管道运行示例:
allocator_example intra-process
你应该得到这样的数字:
Global new was called 15590 times during spin
Global delete was called 15590 times during spin
Allocator new was called 27284 times during spin
Allocator delete was called 27281 times during spin
我们已经捕获了发生在执行路径上的大约2/3
的分配/释放,但是剩下的1/3
来自哪里呢?
事实上,本例使用底层DDS
实现这些分配/释放(操作)。
证明这是超出了本教程的范围,但是你可以查看配置的测试路径,运行的ROS2
持续集成测试,通过代码和数据回溯跟踪,(看看)调用特定的函数是否是由DDS
或者rmw
来实现:
https://github.com/ros2/realtime_support/blob/foxy/tlsf_cpp/test/test_tlsf.cpp#
注意,这个测试没有使用我们刚刚创建的自定义分配器,而是使用TLSF
分配器(见下面)。
6.TLSF分配器
ROS2
支持TLSF
(Two Level Segregate Fit)
分配器,其设计是为了满足实时需求:
https://github.com/ros2/realtime_support/tree/foxy/tlsf_cpp
有关TLSF
的更多信息,请参见
http://www.gii.upv.es/tlsf/
注意,TLSF
分配器是在双gpl /LGPL
许可证下许可的。
使用TLSF
分配器的完整示例如下:
https://github.com/ros2/realtime_support/blob/foxy/tlsf_cpp/example/allocator_example.cpp
其他
感觉这一节课也是高端操作,看得我眼花缭乱,压根没看懂.
这课程是在等毕业证那十几天搞的,室友问,现在在线翻译这么强大,为啥还在这里瞎折腾呢?我说,我的目地是好好认真看一下,了解一下,自己折腾,目前是我想到最好的办法来获得最佳效果,即使这翻译有点别扭,哈哈哈.
#####################
不积硅步,无以至千里
好记性不如烂笔头
感觉有点收获的话,麻烦大大们点赞收藏哈
ROS2学习笔记27--实现自定义内存分配器相关推荐
- C++/C学习笔记(十一)——存储分配器和适配器
C++/C学习笔记(十) --存储分配器和适配器 1.存储分配器 STL容器元素的存储空间是动态分配和释放的,不同的硬件平台和操作系统对内存的管理方法和使用方法各不相同,STL为容器类定义了一个专门负 ...
- JVM学习笔记(四)------内存调优
首先需要注意的是在对JVM内存调优的时候不能只看操作系统级别Java进程所占用的内存,这个数值不能准确的反应堆内存的真实占用情况,因为GC过后这个值是不会变化的,因此内存调优的时候要更多地使用JDK提 ...
- Mr.J-- jQuery学习笔记(十九)--自定义动画实现图标特效
之前有写过自定义动画Mr.J-- jQuery学习笔记(十八)--自定义动画 这次实现一个小demo 图标特效 页面渲染 <!DOCTYPE html> <html lang=&qu ...
- PhalAPI学习笔记 ——— 第一章自定义HelloWorld接口
PhalAPI学习笔记 --- 第一章自定义HelloWorld接口 前言 自定义接口 项目实例 结果 分布解析 结束语 前言 公司业务需要转学PHP,而PHP中一个功能强大且生态链完整的PHP接口框 ...
- 英伟达DeepStream学习笔记27——deepstream下载历史版本
英伟达DeepStream学习笔记27--deepstream下载历史版本 https://docs.nvidia.com/metropolis/deepstream-archive.html htt ...
- 影像组学视频学习笔记(27)-SimpleITK包介绍、Li‘s have a solution and plan.
本笔记来源于B站Up主: 有Li 的影像组学的系列教学视频 本节(27)主要讲解: 功能强大的图像处理工具SimpleITK包 视频中李博士演示了SimpleITK的两个基本功能:图像格式转换以及图像 ...
- ROS2学习笔记(2)什么是ROS2 nodes
什么是ROS2 nodes 先了解一下ROS2 graph.ROS2 graph是一个ROS2元素同时处理数据的网络.它包含了所有可执行程序和它们之间的连接. ROS中每个负责单独功能的模块叫做节点( ...
- ROS2学习笔记(3)什么是ROS2 topics
什么是ROS2 topics 上一篇笔记学习了节点,ROS2正是帮我们可以把一个复杂的系统分解成很多个模块化的节点. topics(话题)是ROS的重要元素,它的作用就充当这些模块化的节点之间交换信息 ...
- C++ Primer Plus学习笔记之类和动态内存分配
前言 个人觉得学习编程最有效的方法是阅读专业的书籍,通过阅读专业书籍可以构建更加系统化的知识体系. 一直以来都很想深入学习一下C++,将其作为自己的主力开发语言.现在为了完成自己这一直以来的心愿,准备 ...
最新文章
- python与mongo_MongoDB与Python的交互
- pycharm快捷使用
- redis 都有哪些数据类型?分别在哪些场景下使用比较合适?
- SAP UI5 应用开发教程之七 - JSON 模型初探
- 数据库服务器网页,服务器 数据库 网页前端
- 结构体跨函数应用(二)
- Servlet案例6:显示用户的上次访问时间
- 【鲁棒控制】平面2R型机器人的鲁棒逆推跟踪控制(matlab实现)
- 初识公有云--公有云的相关概念
- 服务器图纸被自动删除,JavaWeb项目图片消失的原因之一————服务器上图片目录被误删...
- 03 爬虫之selenium模块
- Linux Bash脚本练习2
- itunes刷机一直正在恢复固件要多久_ios刷机报错故障汇总指南
- iOS TableView实现QQ好友列表(一)
- matlab屏保弹球,连续时间下的弹球建模
- 修复DialogFragment Fragment already added 异常
- 产品有感之拼多多——社交更优惠的电商平台
- 尚融宝——阿里云短信验证功能(sms)
- 设计模式学习笔记(一)
- Hadoophbase监控页面未授权访问漏洞处理方案验证过程实现
热门文章
- 开发FireFox浏览器扩展(Extension)并实现与原生应用之间的消息传递
- android+状态栏显示图标大全,Android应用图标在状态栏上显示实现原理
- MySQL数据库(安装配置 语句语法使用 项目中操作MySQL)
- 如何使用文献管理软件EndNote x9 x9.1高效管理科研相关参考文献 相关论文
- 一文读懂——光纤通信技术
- 怎样对接同花顺股票程序化交易接口?
- 30个最佳免费的CSS HTML登录表单模板
- 删除服务 MySQL mysql删除服务 无法删除服务
- 使用运动控制卡,卡顿、蓝屏了怎么办?优易控提供更高级的解决方案
- 从明天起,做一个幸福的人