当前位置:网站首页>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
边栏推荐
猜你喜欢
A single game with goods increased by 100000, and the rural anchor sold men's clothes on top of the list?
NCS Chengdu Xindian interview experience
南京商品房买卖启用电子合同,君子签助力房屋交易在线网签备案
let const
Teach you how to select PCB board by hand (II)
AVL balanced binary search tree
[Yugong series] February 2022 U3D full stack class 005 unity engine view
Compilation and linking of programs
21 general principles of wiring in circuit board design_ Provided by Chengdu circuit board design
数据分片介绍
随机推荐
Basic data types and string types are converted to each other
Oracle makes it clear at one time that a field with multiple separators will be split into multiple rows, and then multiple rows and columns. Multiple separators will be split into multiple rows, and
Greenplum6.x监控软件搭建
Three series of BOM elements
基本数据类型和string类型互相转化
Virtual address space
Greenplum6.x搭建_环境配置
uniapp 微信小程序监测网络
LeetCode 736. Lisp 语法解析
Greenplum 6.x monitoring software setup
Laravel8 uses passport login and JWT (generate token)
最长上升子序列模型 AcWing 1017. 怪盗基德的滑翔翼
POJ - 3784 running medium
[南京大学]-[软件分析]课程学习笔记(一)-introduction
Teach you how to select PCB board by hand (II)
go写一个在一定时间内运行的程序
Exercise arrangement 2.10, 11
Data analysis methodology and previous experience summary 2 [notes dry goods]
9c09730c0eea36d495c3ff6efe3708d8
Rapid integration of authentication services - harmonyos platform