当前位置:网站首页>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
边栏推荐
- Category of IP address
- [MySQL] detailed explanation of trigger content of database advanced
- 最长上升子序列模型 AcWing 1017. 怪盗基德的滑翔翼
- Compilation and linking of programs
- Rapid integration of authentication services - harmonyos platform
- 年薪50w阿里P8亲自下场,教你如何从测试进阶
- 調用華為遊戲多媒體服務的創建引擎接口返回錯誤碼1002,錯誤信息:the params is error
- Quick sorting (detailed illustration of single way, double way, three way)
- oracle一次性说清楚,多种分隔符的一个字段拆分多行,再多行多列多种分隔符拆多行,最终处理超亿亿。。亿级别数据量
- Appeler l'interface du moteur de création du service multimédia de jeu Huawei renvoie le Code d'erreur 1002, le message d'erreur: les paramètres sont l'erreur
猜你喜欢
随机推荐
xray的简单使用
Routing information protocol rip
对API接口或H5接口做签名认证
[machine learning] watermelon book data set_ data sharing
Analysis of abnormal channel number information before and after AGC re signature service
Greenplum6.x-版本变化记录-常用手册
[南京大学]-[软件分析]课程学习笔记(一)-introduction
调用华为游戏多媒体服务的创建引擎接口返回错误码1002,错误信息:the params is error
指针进阶,字符串函数
How to realize the high temperature alarm of the machine room in the moving ring monitoring system
How to realize sliding operation component in fast application
Greenplum 6.x common statements
数据分析方法论与前人经验总结2【笔记干货】
Newly found yii2 excel processing plug-in
南京商品房买卖启用电子合同,君子签助力房屋交易在线网签备案
redis故障处理 “Can‘t save in background: fork: Cannot allocate memory“
QT charts use (rewrite qchartview to realize some custom functions)
Opencv converts 16 bit image data to 8 bits and 8 to 16
21 general principles of wiring in circuit board design_ Provided by Chengdu circuit board design
ncs成都新电面试经验