当前位置:网站首页>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
边栏推荐
- IP地址的类别
- 基本数据类型和string类型互相转化
- LeetCode 736. Lisp 语法解析
- Tronapi wave field interface - source code without encryption - can be opened twice - interface document attached - package based on thinkphp5 - detailed guidance of the author - July 6, 2022 - Novice
- opencv 将16位图像数据转为8位、8转16
- Componentspace2022, assertions, protocols, bindings, and configuration files
- Mountaineering team (DFS)
- redis故障处理 “Can‘t save in background: fork: Cannot allocate memory“
- 9c09730c0eea36d495c3ff6efe3708d8
- [Yugong series] February 2022 U3D full stack class 008 - build a galaxy scene
猜你喜欢
数字三角形模型 AcWing 1027. 方格取数
为什么要选择云原生数据库
Other 7 features of TCP [sliding window mechanism ▲]
[hard core science popularization] working principle of dynamic loop monitoring system
登山小分队(dfs)
Greenplum 6.x build_ Environment configuration
Greenplum 6.x reinitialization
Image segmentation in opencv
Nanjing commercial housing sales enabled electronic contracts, and Junzi sign assisted in the online signing and filing of housing transactions
联想混合云Lenovo xCloud:4大产品线+IT服务门户
随机推荐
Three usage scenarios of annotation @configurationproperties
如何在HarmonyOS应用中集成App Linking服务
QT charts use (rewrite qchartview to realize some custom functions)
Category of IP address
channel. Detailed explanation of queuedeclare parameters
uniapp 微信小程序监测网络
Novice entry SCM must understand those things
What is the method of manual wiring in PCB design in 22protel DXP_ Chengdu electromechanical Development Undertaking
使用AGC重签名服务前后渠道号信息异常分析
Greenplum 6.x build_ Environment configuration
Calling the creation engine interface of Huawei game multimedia service returns error code 1002, error message: the params is error
Mock. JS usage details
指针进阶,字符串函数
Opencv converts 16 bit image data to 8 bits and 8 to 16
年薪50w阿裏P8親自下場,教你如何從測試進階
测试人一定要会的技能:selenium的三种等待方式解读,清晰明了
Lenovo hybrid cloud Lenovo xcloud: 4 major product lines +it service portal
What are the advantages of commas in conditional statements- What is the advantage of commas in a conditional statement?
Other 7 features of TCP [sliding window mechanism ▲]
Data analysis methodology and previous experience summary 2 [notes dry goods]