当前位置:网站首页>Implement custom memory allocator
Implement custom memory allocator
2022-07-07 08:50:00 【Hermit_ Rabbit】
0. brief introduction
This tutorial will teach you how to integrate custom allocators for publishers and listeners , In order to implement ROS
Node will never call the default heap allocator . The code of this tutorial is here (https://github.com/ros2/demos/blob/foxy/demo_nodes_cpp/src/topics/allocator_tutorial.cpp
).
1. background
Suppose you want to write real-time, secure code , And you've heard about calling in the real-time critical area “new”
Many dangers of , Because the default heap allocator on most platforms is uncertain .
By default , many c++
The standard library structure implicitly allocates memory as it grows , such as std::vector
. However , These data structures also accept a “Allocator”
Template parameter . If you specify a custom allocator for one of these data structures , It will use this allocator instead of the system allocator for you to grow or shrink data structures . Your custom allocator can pre allocate the memory pool on the stack , This may be more suitable for real-time applications .
stay ROS2 C++
Client library (rclcpp
) in , We follow and c++
A similar concept to the standard library . Publisher 、 Listeners and Executor
Accept one Allocator
Template parameter , This parameter controls the allocation of the entity during execution .
2. Write a allocator
To write a with ROS2
Distributor interface compatible distributor , Your distributor must be compatible with c++
The standard library allocator interface is compatible .
c++ 11
The library provides a file named allocator_traits
Things that are .c++ 11
The standard stipulates , The custom allocator only needs to meet the minimum set of requirements required to allocate and free memory in a standard way .allocator_traits
It is a general structure , It is based on The allocator written with minimum requirements fills in other features of the allocator .
for example , The following custom allocator declaration will satisfy allocator_traits
( Of course , You still need to implement the declared function in this structure ):
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;
Then you can visit by allocator_traits
Fill in other functions and allocator members :
std::allocator_traits<custom_allocator<T>>::construct(...)
To understand allocator_traits
All the functions of , Please see the https://en.cppreference.com/w/cpp/memory/allocator_traits
.
However , Some only support some c++ 11
The compiler , Such as GCC 4.8
, Allocators are still needed to implement large amounts of sample code , To deal with the standard library structure ( Such as vector
and string
), Because these structures are not used internally allocator_traits
. therefore , If you are using a partial support c++ 11
The compiler , Your dispenser will need to look more like this :
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. Write a main Example
Effective c++
Behind the dispenser , It must be passed to the publisher as a shared pointer 、 Listeners and executors .
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);
You also need to use the allocator to allocate any messages that pass along the execution code path .
auto alloc = std::make_shared<MyAllocator<void>>();
Once you instantiate a node and add an actuator to it , It's time to start spinning :
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. Pass the allocator to the process internal pipeline
Even if we instantiate the publisher and listener in the same process , We haven't used in-process channels yet .
IntraProcessManager
Is a class that is usually hidden from users , But in order to pass the custom allocator to it , We need to pass from rclcpp
Environment get it , To make it public .IntraProcessManager
Several standard library structures are used , So if there is no custom allocator , It will call the default new .
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);
Make sure that after the node is constructed in this way , Instantiate publishers and listeners .
5. Test and verify the code
How do you know , Your custom allocator is actually being called ?
The obvious way to do this is to calculate the allocate
and deallocate
Function call ( frequency ), And compare it with new
and delete
Call to ( frequency ) Compare .
Add a count to the custom allocator ( The function is ) It's simple :
T * allocate(size_t size, const void * = 0) {
// ...
num_allocs++;
// ...
}
void deallocate(T * ptr, size_t size) {
// ...
num_deallocs++;
// ...
}
You can also cover the whole situation new
and delete
The operator :
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;
}
}
among , The variable we increment is only a global static integer , and is_running
Is a global static Boolean , Calling spin
Previously switched .
Example (https://github.com/ros2/demos/blob/foxy/demo_nodes_cpp/src/topics/allocator_tutorial.cpp
) Executable print variable value . To run the sample executable , Please use :
allocator_example
perhaps , Run the example using an in-process pipeline :
allocator_example intra-process
You should get such a number :
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
We have captured about 2/3
The distribution of / Release , But the rest 1/3
Where does it come from ?
in fact , This example uses the underlying DDS
Implement these allocations / Release ( operation ).
Prove that this is beyond the scope of this tutorial , But you can check the configured test path , Running ROS2
Continuous integration testing , Trace through code and data backtracking ,( have a look ) Whether a specific function is called by DDS
perhaps rmw
To achieve :
https://github.com/ros2/realtime_support/blob/foxy/tlsf_cpp/test/test_tlsf.cpp#
Be careful , This test does not use the custom allocator we just created , But use TLSF
distributor ( See below ).
6.TLSF distributor
ROS2
Support TLSF
(Two Level Segregate Fit)
distributor , It is designed to meet real-time requirements :
https://github.com/ros2/realtime_support/tree/foxy/tlsf_cpp
of TLSF
For more information , Please see the
http://www.gii.upv.es/tlsf/
Be careful ,TLSF
The distributor is in double gpl /LGPL
Licensed under the license .
Use TLSF
A complete example of the distributor is as follows :
https://github.com/ros2/realtime_support/blob/foxy/tlsf_cpp/example/allocator_example.cpp
边栏推荐
猜你喜欢
数据分片介绍
All about PDF crack, a complete solution to meet all your PDF needs
Greenplum6.x监控软件搭建
Implementation of navigation bar at the bottom of applet
Why choose cloud native database
如何在快应用中实现滑动操作组件
[Yugong series] February 2022 U3D full stack class 005 unity engine view
Category of IP address
[Yu Yue education] higher vocational English reference materials of Nanjing Polytechnic University
Through the "last mile" of legal services for the masses, fangzheng Puhua labor and personnel law self-service consulting service platform has been frequently "praised"
随机推荐
[machine learning] watermelon book data set_ data sharing
You should use Google related products with caution
Other 7 features of TCP [sliding window mechanism ▲]
使用AGC重签名服务前后渠道号信息异常分析
Interpolation lookup (two methods)
测试踩坑 - 当已有接口(或数据库表中)新增字段时,都需要注意哪些测试点?
String operation
年薪50w阿裏P8親自下場,教你如何從測試進階
Novice entry SCM must understand those things
9c09730c0eea36d495c3ff6efe3708d8
说一个软件创业项目,有谁愿意投资的吗?
Tronapi-波场接口-源码无加密-可二开--附接口文档-基于ThinkPHP5封装-作者详细指导-2022年7月6日-新手快速上手-可无缝升级tp6版本
Input and output of floating point data (C language)
xray的简单使用
Virtual address space
LeetCode 715. Range 模块
阿里p8手把手教你,自动化测试应该如何实现多线程?赶紧码住
[南京大学]-[软件分析]课程学习笔记(一)-introduction
selenium自动化集成,八年测试经验软测工程师,一篇文章带你学懂
南京商品房买卖启用电子合同,君子签助力房屋交易在线网签备案