跳转到内容

优化 C++/编写高效代码/分配和释放

来自维基教科书,开放的书籍,开放的世界

与自动内存分配和释放相比,动态内存分配和释放是非常慢的操作。换句话说,堆比栈慢得多。

此外,动态分配会产生每次分配的开销,会导致虚拟内存碎片化,并导致不良的数据局部性引用,进而导致处理器数据缓存和虚拟内存空间的糟糕使用。

在 C 语言中,动态内存分配/释放使用 `malloc` 和 `free` 标准库函数完成。在 C++ 中,虽然这些函数仍然可用,但通常使用 `new`、`new[]`、`delete` 和 `delete[]` 运算符。

减少分配次数的一个明显方法是减少构造对象的数量,为此,您应该参考本章的“构造和析构”部分。

这里我们集中于减少给定数量的 `new` 运算符调用时的分配次数的指南。

固定长度数组

[编辑 | 编辑源代码]

如果静态或非大型数组具有编译时常量长度,则应使用 C 语言数组、std::array 或来自 Boost 库的 `array` 对象,而不是 `vector` 对象。

`vector` 在动态分配的缓冲区中存储数据,而数组在对象本身内部分配数据。这避免了重复的动态内存分配/释放,并有利于数据局部性。

如果数组很大,这些优势就会减少,避免使用过多的堆栈空间变得更加重要。

分配许多小对象

[编辑 | 编辑源代码]

如果您必须分配许多相同大小的对象,请使用块分配器。

一个 块分配器(又称池分配器)分配中等至大型内存块,并提供分配/释放较小固定大小块的服务。它允许高速分配/释放、低内存碎片化以及对数据缓存和虚拟内存的有效利用。

特别是,这种分配器可以大大提高 `std::list`、`std::set`、`std::multiset`、`std::map` 和 `std::multimap` 标准容器的性能。

如果您的标准库实现尚未为这些容器使用块分配器,您应该获取一个并将其指定为这些容器模板实例的模板参数。Boost 提供了两个可自定义的块分配器,`pool_allocator``fast_pool_allocator`。其他池分配器库可以在万维网上找到。始终先进行测量,以找到最适合手头工作的分配器。

将元素追加到集合

[编辑 | 编辑源代码]

当您必须将元素追加到集合时,使用 `push_back` 追加单个元素,使用 `insert` 追加一系列元素,并使用 `back_inserter` 使 STL 算法将元素追加到序列。

`push_back` 函数保证摊销线性时间,因为在 `vector` 的情况下,它会以指数方式增加容量。

`back_inserter` 类在内部调用 `push_back` 函数。

`insert` 函数允许以优化方式插入整个序列,因此单个 `insert` 调用比多次调用 `push_back` 更快。

华夏公益教科书