当前位置:网站首页>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
边栏推荐
- 【踩坑】nacos注册一直连接localhost:8848,no available server
- ncs成都新电面试经验
- Why choose cloud native database
- 为什么要选择云原生数据库
- 测试人一定要会的技能:selenium的三种等待方式解读,清晰明了
- Mountaineering team (DFS)
- 南京商品房买卖启用电子合同,君子签助力房屋交易在线网签备案
- 9c09730c0eea36d495c3ff6efe3708d8
- Go write a program that runs within a certain period of time
- Uniapp wechat applet monitoring network
猜你喜欢

About using CDN based on Kangle and EP panel

Interpolation lookup (two methods)

leetcode135. Distribute candy

Greenplum6.x重新初始化

How to realize the high temperature alarm of the machine room in the moving ring monitoring system

Teach you how to select PCB board by hand (II)

opencv之图像分割

Rapid integration of authentication services - harmonyos platform

What is the method of manual wiring in PCB design in 22protel DXP_ Chengdu electromechanical Development Undertaking
![Other 7 features of TCP [sliding window mechanism ▲]](/img/ff/c3f52a7b89804acfd0c4f3b78bc4a0.jpg)
Other 7 features of TCP [sliding window mechanism ▲]
随机推荐
9c09730c0eea36d495c3ff6efe3708d8
Tronapi-波场接口-源码无加密-可二开--附接口文档-基于ThinkPHP5封装-作者详细指导-2022年7月6日-新手快速上手-可无缝升级tp6版本
Other 7 features of TCP [sliding window mechanism ▲]
Arm GIC (IV) GIC V3 register class analysis notes.
Tips for using jeditabletable
指针进阶,字符串函数
All about PDF crack, a complete solution to meet all your PDF needs
Greenplum6.x搭建_安装
Explain Huawei's application market in detail, and gradually reduce 32-bit package applications and strategies in 2022
数字三角形模型 AcWing 1027. 方格取数
ncs成都新電面試經驗
let const
Greenplum6.x常用语句
【微信小程序:缓存操作】
Redis fault handling "can't save in background: fork: cannot allocate memory“
POJ - 3616 Milking Time(DP+LIS)
Input and output of floating point data (C language)
xray的简单使用
leetcode135. Distribute candy
[MySQL] detailed explanation of trigger content of database advanced