• C++容器工作效率-内存操作


    在之前轨迹的中看到了一段代码:

    TrajectoryAnalyzer()类中一个 构造函数,函数接收的是轨迹信息。在个构造函数中使用std::vector管理轨迹点,其实是逐个轨迹点push_back。

    同时又看了一地段Eigen的代码,同样使用了std:vector,但是使用了自己内存分配器:

    template>

    class vector: protect _Vector_base

    同样的vector使用默认的或者用户实现的分配器件在性能上有什么区别?vector的表现又是如何?

    先看一段测试程序:

    1. #include
    2. #include
    3. #include "time.hpp"
    4. struct Point
    5. {
    6. Point() = default;
    7. Point(double x_,double y_,double z_,double h_,double v_)
    8. {
    9. //std::cout<<"Create Point with params : "<
    10. x = x_;
    11. y = y_;
    12. z = z_;
    13. heading = h_;
    14. v = v_;
    15. }
    16. double x;
    17. double y;
    18. double z;
    19. double heading;
    20. double v;
    21. };
    22. template<typename T>
    23. struct MyAllocator {
    24. using value_type = T;
    25. MyAllocator() noexcept {}
    26. template<typename U>
    27. MyAllocator(const MyAllocator&) noexcept {}
    28. template<class... Args>
    29. void construct(T* p, Args&&... args)
    30. {
    31. //std::cout << "Constructing one at "<< p <
    32. ::new(p) T(std::forward(args)...);
    33. };
    34. T* allocate(std::size_t n) {
    35. std::cout<<"Try to allocate "<<sizeof(T)*n<<" bytes"<
    36. if (n > std::size_t(-1) / sizeof(T)) {
    37. throw std::bad_alloc();
    38. }
    39. if (auto p = static_cast(std::malloc(n * sizeof(T)))) {
    40. return p;
    41. }
    42. throw std::bad_alloc();
    43. }
    44. void deallocate(T* p, std::size_t) noexcept {
    45. std::cout<<"Release all the region"<
    46. std::free(p);
    47. }
    48. };
    49. template <typename T, typename Allocator = MyAllocator>
    50. using MyVector = std::vector;
    51. int main() {
    52. MyVector myVector;
    53. myVector.reserve(1024);
    54. std::cout<<"Try to push 1024 elements "<
    55. std::cout<Now()<
    56. for(int elem = 0;elem < 1024;elem++)
    57. myVector.emplace_back(Point(1.0,2.0,3.0,4.0,5.0));
    58. std::cout<Now()<
    59. //test last vector elem
    60. std::cout << myVector[1020].x<
    61. std::cout << myVector[1020].y<
    62. std::cout << myVector[1020].z<
    63. std::cout << myVector[1020].heading<
    64. std::cout << myVector[1020].v<
    65. std::vector v;
    66. v.reserve(1024);
    67. std::cout<Now()<
    68. for(int elem = 0;elem < 1024;elem++)
    69. v.emplace_back(Point(1.0,2.0,3.0,4.0,5.0));
    70. //v.push_back(Point(1.0,2.0,3.0,4.0,5.0));
    71. std::cout<Now()<
    72. return 0;
    73. }

    解释一下提交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插入数据比较:

    1. Try to allocate 40960 bytes
    2. Try to push 1024 elements
    3. 2023-09-15 16:05:39.879776909
    4. 2023-09-15 16:05:39.879906062
    5. 1
    6. 2
    7. 3
    8. 4
    9. 5
    10. 2023-09-15 16:05:39.879963790
    11. 2023-09-15 16:05:39.880002639
    12. 12953 ns
    13. 38849 ns

    用户分配器使用placement new在已分配好的内存上构造13微秒

    使用默认的分配器耗时明显比多。

    2,默认的分配方式emplace_back插入数据和用户自定义的allocator插入数据比较:

    1. 2023-09-15 16:46:17.482801219
    2. 2023-09-15 16:46:17.482927142
    3. 1
    4. 2
    5. 3
    6. 4
    7. 5
    8. 2023-09-15 16:46:17.482970918
    9. 2023-09-15 16:46:17.483009799
    10. 125923 ns
    11. 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