August 2021
Intermediate to advanced
450 pages
5h 35m
Chinese
标准库容器默认使用new分配空间。操作符new和delete提供了一种通用的自由存储空间(也被称为动态内存或堆),可以保存大小任意且生命周期可控的对象。这意味着时间和空间上的额外开销,而这种开销在很多特殊情况下是可以消除的。因此,标准库容器提供了在需要时安装具有特殊语义的分配器的机制。这一机制已被用来解决很多在意性能(如池分配器)、安全性(删除时清理内存的分配器)、每线程分配以及非一致内存架构(在特殊内存上分配,这种内存具有相匹配的指针类型)的应用。本书不会讨论这些重要但非常特殊的技术,这些技术通常也是高级技术。但是,我会介绍一个由现实世界问题激发的例子,它可用池分配器解决[Zubkov,2017]。
一个重要的、长时间运行的系统使用一个事件队列(参见15.6节),其中每个事件是一个vector,以shared_ptr传递。这样,一个事件的最后一个用户会隐式地删除它:
从逻辑角度看,这个版本工作得很好。它逻辑简单,因此代码健壮、可维护。不幸的是,这个版本会导致大量碎片。在16个生产者和4个消费者之间传递100000个事件后,会消耗超过6GB的内存。
碎片问题的传统解决方案是用池分配器重写代码。池分配器是一种管理单一固定大小对象的分配器,每次要为很多对象分配空间,而非为单个对象分配。幸运的是,C++对此提供了直接支持。池分配器定义在std的pmr(“多态内存资源”)子名字空间中:
现在,在16个生产者和4个消费者之间传递100000个事件之后,只会消耗少于3MB的内存。这有2000倍的性能提升!自然,真正使用的内存量(与碎片浪费的内存相对)并未改变。在消除碎片后,随着时间推移内存使用保持稳定,从而内存可以持续数月地运行。 ...
Read now
Unlock full access