现代 C++ 编程实战
吴咏炜
前 Intel 资深软件架构师
34199 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 51 讲
加餐 (1讲)
现代 C++ 编程实战
15
15
1.0x
00:00/00:00
登录|注册

34|快速分配和释放内存:内存池

容器的 Allocator 参数
线程本地内存池
分配器支持内存池
使用内存池的性能对比
类内声明 new 和 delete
分配器
类特定的分配和释放函数
内存块链表管理
内存块数据结构
内存块的大小和管理
减少系统内存分配器请求次数
[2] GNU, “The GNU allocator”
[1] Google, “gperftools”
容器结点类型发现
对象计数和内存释放
练习内存池代码
知道何时和如何实现内存池
内存池的测试和实现
解决方案
全局对象与内存池析构顺序
内存池应用
对象内存池
基本策略
性能测试与结果
多态分配器
使用 tcmalloc 的性能提升
再次插入的性能提升
不同平台和编译器的差异
测量时间开销
插入和删除操作
产生随机数
创建测试用例
可能使对象交互变得复杂
可能导致操作系统难以回收内存
通用内存分配器可能已经足够快
减少内存分配和释放的空间开销
减少内存分配和释放的时间开销
参考资料
课后思考
内容小结
生命周期陷阱
自定义内存池
PMR 内存池
测试结果
性能测试
为什么不使用内存池
为什么使用内存池
内存池的概念与应用

该思维导图由 AI 生成,仅供参考

你好,我是吴咏炜。
在上一讲讲过了性能测试之后,我们终于可以回到内存池这个话题,来深入讨论一下了。

一个测试用例

如果你想用内存池,那我的第一个问题就是,你到底是不是需要使用内存池?
下面是一些你可能想使用内存池的理由:
希望减少内存分配和释放的时间开销——更快的分配和释放
希望减少内存分配和释放的空间开销——更少的总体内存使用
下面则是一些反对使用内存池的理由:
你的通用内存分配器可能已经足够快了
使用内存池可能导致操作系统更难回收你已经不再需要的内存
使用内存池可能使得你的对象较难跟其他对象进行交互(参考第 32 讲,在 PMR 之前分配器是容器类型的一部分)
当然,既然你看到这里了,你肯定是想要使用内存池的。不过,我们需要能够衡量使用内存池的效果,所以我们需要进行测试。
如果你想要进行某个操作的性能测试,你就需要某种“典型场景”。作为例子,我这儿拿一个掺入了随机操作的过程当作测试场景。具体来说,我做的事情是:
产生随机数
把这些随机数插入到一个 unordered_set 中,测量所需的时间
把这些随机数从这个 unordered_set 里逐个删除,测量所需的时间
再把这些随机数重新插入到 unordered_set 中,测量所需的时间
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了内存池技术的使用场景、测试方法和实现策略。通过介绍内存池的理由和反对意见,并展示了内存池的作用及不同平台和编译器对性能的影响。详细介绍了C++的内存池资源(PMR)和多态分配器,并讨论了自定义内存池的基本策略,包括减少向系统内存分配器请求内存的次数、对象类型和大小的关系、内存返回策略等。文章还探讨了内存池在类特定的分配和释放函数以及分配器中的应用,展示了内存池在不同场景下的性能提升。通过具体的测试和实现策略,本文深入探讨了内存池的使用和优化技术,对于需要了解内存池的读者具有一定的参考价值。文章还提出了课后思考问题,鼓励读者练习代码并尝试不同的解决方案,以加深对内存池的理解。整体而言,本文对内存池技术进行了全面而深入的探讨,为读者提供了宝贵的技术参考。 参考资料: 1. Google, “gperftools”. <https://github.com/gperftools/gperftools> 2. GNU, “The GNU allocator”. <https://www.gnu.org/software/libc/manual/html_node/The-GNU-Allocator.html>

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《现代 C++ 编程实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(9)

  • 最新
  • 精选
  • 李云龙
    老师,为什么第二次插入的时候会变快?似乎所有的性能测试都是这样的,跟局部性和缓存有关系吗?

    作者回复: 对,内存已经变“热”了。

    2023-11-13归属地:韩国
    1
  • 怪兽
    请问一下老师,在"内存池应用:类特定的分配和释放函数"小节里,原文说"不管写不写 static,这两个成员函数都是静态的",没明白为什么不写static也是静态的。

    作者回复: 规定。这两个必须是静态的,否则没有意义。(对于已经存在的对象怎么 new?)

    2022-12-06归属地:上海
    1
  • Geek_595be5
    上面例子中定义内存数据storage_用的array类型,请问这是单纯出于个人习惯偏好呢?还是array比vector在这里更合适呢?

    作者回复: 我就是要一次性分配一块内存出来,array 足矣。这里使用 vector 在时间和空间上都会更差。

    2023-08-30归属地:新加坡
  • coming
    t1 = rdtsc(); for (int i = 0; i < LOOPS; ++i) { for (auto &ptr : ptr_array1) { ptr = (normal::Obj*)tc_malloc(sizeof(normal::Obj)); } for (auto ptr : ptr_array1) { tc_free(ptr); } } t2 = rdtsc(); 这种使用tc_malloc方式正确吧, 但是很奇怪啊, 显示CPU周期比较, pooled最慢了 68 cycles for each allocation and deallocations on normal Obj 94 cycles for each allocation and deallocations on pooled Obj 51 cycles for each allocation and deallocations on tcmalloced Obj

    作者回复: 开优化了吗?你的数字比我的大多了。 另外,用 tcmalloc 不需要改代码的。直接链接 libtcmalloc.so 就行。

    2023-07-17归属地:上海
    2
  • Geek_power
    老师,请问下参考资料1是您极力推荐的内存管理库吗,我准备研究并往项目使用

    作者回复: Linux 下推荐使用。建议先使用 profiling 工具或自己测试性能来比较一下。 任何性能问题都需要测量来得到最后决定需要的数据。

    2022-12-28归属地:上海
  • 怪兽
    老师,还有个疑问,在"生命周期陷阱"小节里,有4点描述。其中第2点说"它的析构函数会挂到线程退出需要执行的代码中。注意这比第 1 步要晚",这里的意思是指内存池的析构比全局对象的析构要晚吧。然后第3点又说,"后构造的先析构",意思是内存池的析构比全局对象的析构要早吧?这不矛盾了吗?我哪里理解出问题了?麻烦老师指正一下。

    作者回复: 不是。第二点和第三点是一致的。后构造的先析构,晚挂到退出执行链上的代码先执行。

    2022-12-06归属地:上海
  • 向我开炮 东狗子
    这就是二十年的功力吗
    2023-01-16归属地:陕西
    2
  • 03125555
    居然还在更新!
    2022-04-06
    1
    1
  • DDRH
    居然还在更新!太赞了
    2023-09-10归属地:广东
收起评论
显示
设置
留言
9
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部