概要:这篇内容主要介绍如何实现自定义内存分配器

环境: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,仍然需要分配器来实现大量的样本代码,以处理标准库结构(如vectorstring),因为这些结构在内部不使用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.测试和验证代码

你如何知道,你的自定义分配器实际上正在被调用?

显而易见的做法是计算对自定义分配器的allocatedeallocate函数的调用(次数),并将其与对newdelete的调用(次数)进行比较。

向自定义分配器添加计数(功能是)很简单:

T * allocate(size_t size, const void * = 0) {// ...num_allocs++;// ...
}void deallocate(T * ptr, size_t size) {// ...num_deallocs++;// ...
}

你也可以覆盖全局newdelete操作符:

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--实现自定义内存分配器相关推荐

  1. C++/C学习笔记(十一)——存储分配器和适配器

    C++/C学习笔记(十) --存储分配器和适配器 1.存储分配器 STL容器元素的存储空间是动态分配和释放的,不同的硬件平台和操作系统对内存的管理方法和使用方法各不相同,STL为容器类定义了一个专门负 ...

  2. JVM学习笔记(四)------内存调优

    首先需要注意的是在对JVM内存调优的时候不能只看操作系统级别Java进程所占用的内存,这个数值不能准确的反应堆内存的真实占用情况,因为GC过后这个值是不会变化的,因此内存调优的时候要更多地使用JDK提 ...

  3. Mr.J-- jQuery学习笔记(十九)--自定义动画实现图标特效

    之前有写过自定义动画Mr.J-- jQuery学习笔记(十八)--自定义动画 这次实现一个小demo 图标特效 页面渲染 <!DOCTYPE html> <html lang=&qu ...

  4. PhalAPI学习笔记 ——— 第一章自定义HelloWorld接口

    PhalAPI学习笔记 --- 第一章自定义HelloWorld接口 前言 自定义接口 项目实例 结果 分布解析 结束语 前言 公司业务需要转学PHP,而PHP中一个功能强大且生态链完整的PHP接口框 ...

  5. 英伟达DeepStream学习笔记27——deepstream下载历史版本

    英伟达DeepStream学习笔记27--deepstream下载历史版本 https://docs.nvidia.com/metropolis/deepstream-archive.html htt ...

  6. 影像组学视频学习笔记(27)-SimpleITK包介绍、Li‘s have a solution and plan.

    本笔记来源于B站Up主: 有Li 的影像组学的系列教学视频 本节(27)主要讲解: 功能强大的图像处理工具SimpleITK包 视频中李博士演示了SimpleITK的两个基本功能:图像格式转换以及图像 ...

  7. ROS2学习笔记(2)什么是ROS2 nodes

    什么是ROS2 nodes 先了解一下ROS2 graph.ROS2 graph是一个ROS2元素同时处理数据的网络.它包含了所有可执行程序和它们之间的连接. ROS中每个负责单独功能的模块叫做节点( ...

  8. ROS2学习笔记(3)什么是ROS2 topics

    什么是ROS2 topics 上一篇笔记学习了节点,ROS2正是帮我们可以把一个复杂的系统分解成很多个模块化的节点. topics(话题)是ROS的重要元素,它的作用就充当这些模块化的节点之间交换信息 ...

  9. C++ Primer Plus学习笔记之类和动态内存分配

    前言 个人觉得学习编程最有效的方法是阅读专业的书籍,通过阅读专业书籍可以构建更加系统化的知识体系. 一直以来都很想深入学习一下C++,将其作为自己的主力开发语言.现在为了完成自己这一直以来的心愿,准备 ...

最新文章

  1. python与mongo_MongoDB与Python的交互
  2. pycharm快捷使用
  3. redis 都有哪些数据类型?分别在哪些场景下使用比较合适?
  4. SAP UI5 应用开发教程之七 - JSON 模型初探
  5. 数据库服务器网页,服务器 数据库 网页前端
  6. 结构体跨函数应用(二)
  7. Servlet案例6:显示用户的上次访问时间
  8. 【鲁棒控制】平面2R型机器人的鲁棒逆推跟踪控制(matlab实现)
  9. 初识公有云--公有云的相关概念
  10. 服务器图纸被自动删除,JavaWeb项目图片消失的原因之一————服务器上图片目录被误删...
  11. 03 爬虫之selenium模块
  12. Linux Bash脚本练习2
  13. itunes刷机一直正在恢复固件要多久_ios刷机报错故障汇总指南
  14. iOS TableView实现QQ好友列表(一)
  15. matlab屏保弹球,连续时间下的弹球建模
  16. 修复DialogFragment Fragment already added 异常
  17. 产品有感之拼多多——社交更优惠的电商平台
  18. 尚融宝——阿里云短信验证功能(sms)
  19. 设计模式学习笔记(一)
  20. Hadoophbase监控页面未授权访问漏洞处理方案验证过程实现

热门文章

  1. 开发FireFox浏览器扩展(Extension)并实现与原生应用之间的消息传递
  2. android+状态栏显示图标大全,Android应用图标在状态栏上显示实现原理
  3. MySQL数据库(安装配置 语句语法使用 项目中操作MySQL)
  4. 如何使用文献管理软件EndNote x9 x9.1高效管理科研相关参考文献 相关论文
  5. 一文读懂——光纤通信技术
  6. 怎样对接同花顺股票程序化交易接口?
  7. 30个最佳免费的CSS HTML登录表单模板
  8. 删除服务 MySQL mysql删除服务 无法删除服务
  9. 使用运动控制卡,卡顿、蓝屏了怎么办?优易控提供更高级的解决方案
  10. 从明天起,做一个幸福的人