在之前轨迹的中看到了一段代码:
TrajectoryAnalyzer()类中一个 构造函数,函数接收的是轨迹信息。在个构造函数中使用std::vector管理轨迹点,其实是逐个轨迹点push_back。
同时又看了一地段Eigen的代码,同样使用了std:vector,但是使用了自己内存分配器:
template
class vector: protect _Vector_base
同样的vector使用默认的或者用户实现的分配器件在性能上有什么区别?vector的表现又是如何?
先看一段测试程序:
- #include
- #include
- #include "time.hpp"
-
- struct Point
- {
- Point() = default;
- Point(double x_,double y_,double z_,double h_,double v_)
- {
- //std::cout<<"Create Point with params : "<
- x = x_;
- y = y_;
- z = z_;
- heading = h_;
- v = v_;
- }
-
- double x;
- double y;
- double z;
- double heading;
- double v;
- };
-
-
- template<typename T>
- struct MyAllocator {
- using value_type = T;
-
- MyAllocator() noexcept {}
-
- template<typename U>
- MyAllocator(const MyAllocator&) noexcept {}
-
- template<class... Args>
- void construct(T* p, Args&&... args)
- {
- //std::cout << "Constructing one at "<< p <
- ::new(p) T(std::forward
(args)...); - };
-
- T* allocate(std::size_t n) {
- std::cout<<"Try to allocate "<<sizeof(T)*n<<" bytes"<
- if (n > std::size_t(-1) / sizeof(T)) {
- throw std::bad_alloc();
- }
- if (auto p = static_cast
(std::malloc(n * sizeof(T)))) { - return p;
- }
- throw std::bad_alloc();
- }
-
- void deallocate(T* p, std::size_t) noexcept {
- std::cout<<"Release all the region"<
- std::free(p);
- }
- };
-
- template <typename T, typename Allocator = MyAllocator
> - using MyVector = std::vector
; -
- int main() {
-
- MyVector
myVector; - myVector.reserve(1024);
-
- std::cout<<"Try to push 1024 elements "<
-
- std::cout<
Now()< - for(int elem = 0;elem < 1024;elem++)
- myVector.emplace_back(Point(1.0,2.0,3.0,4.0,5.0));
- std::cout<
Now()< -
- //test last vector elem
- std::cout << myVector[1020].x<
- std::cout << myVector[1020].y<
- std::cout << myVector[1020].z<
- std::cout << myVector[1020].heading<
- std::cout << myVector[1020].v<
-
- std::vector
v; - v.reserve(1024);
-
- std::cout<
Now()< - for(int elem = 0;elem < 1024;elem++)
- v.emplace_back(Point(1.0,2.0,3.0,4.0,5.0));
- //v.push_back(Point(1.0,2.0,3.0,4.0,5.0));
- std::cout<
Now()< -
- return 0;
- }
解释一下提交allocator是一个类,类的表达是有格式的。按照格式分别实现:
1,分配内存 - 使用operator new(只分配不构造)
2,销毁内存 - free(看来operator new就是malloc)
3,构造对象 - placement new (在一个地址上构造一个对象)
4,插入对象 - (1)当用户使用自己的分配器,并且有construct函数时候,不论push_back或emplace_back一律调用construct在容器中构造对象。
- (2)当使用默认的分配器时候,push_back和emplace_back的表现不一致,push_back耗时明显大于emplace_back。
5,时间函数 - 前后对比,判断耗时。
下面把测试结果分别显示一下:
测试环境:jetson orin ubuntu 20.04
1,默认的分配方式push_back插入数据和用户自定义的allocator插入数据比较:
- Try to allocate 40960 bytes
- Try to push 1024 elements
- 2023-09-15 16:05:39.879776909
- 2023-09-15 16:05:39.879906062
- 1
- 2
- 3
- 4
- 5
- 2023-09-15 16:05:39.879963790
- 2023-09-15 16:05:39.880002639
-
- 12953 ns
- 38849 ns
用户分配器使用placement new在已分配好的内存上构造13微秒
使用默认的分配器耗时明显比多。
2,默认的分配方式emplace_back插入数据和用户自定义的allocator插入数据比较:
- 2023-09-15 16:46:17.482801219
- 2023-09-15 16:46:17.482927142
- 1
- 2
- 3
- 4
- 5
- 2023-09-15 16:46:17.482970918
- 2023-09-15 16:46:17.483009799
-
-
- 125923 ns
- 38881 ns
使用emplace_back速度优于用户实现的插入操作。
总结:
1,我们看到代码中针对vector先使用reserve操作,预分配了一块内存,因此在插入过程中没有动态的更具内存大小实时调整,vector的调整是有拷贝的,把旧的数据内容拷贝到新的内存空间中。因此,vector预留内存的做法耗时较小,可以和没有reserve的vector比较一下:reserve预先分配内存方式下耗时小。
2,如果真有效率考量的话,还是建议使用emplace_back的操作,原理就是placement_new操作。在一块已知的内存上构造一个元素。
3,再次体验了一下C++中内存操作,作为语言C++考虑确实比较全面:
(1),只分配不构造
(2),分配和构造
(3),在一块空闲内存上分配对象
这也是new的三种不同用法。
谢谢
-
相关阅读:
基于Nodejs+vue开发实现酒店管理系统
CCF中国开源大会专访 | 刘旭东:着眼“开源联合”,实现“聚力共赢”
Java反编译工具 JD-GUI安装使用
RestTemplate获取json数组
腾然教育MCN覃小龙公子:覃宣量2022年2岁10个月亲子照
Spring Cloud Gateway3.x自定义Spring Cloud Loadbalancer负载均衡策略以及实现动态负载均衡策略的方案
数据结构和算法 IV
MySQL 索引
python计算双色球数字概率,python生成随机双色球
地产高质量发展时代:房企为何需要“利他思维”?
-
原文地址:https://blog.csdn.net/edwardlulinux/article/details/132906601