为什么 std::set 容器使用的内存比其数据大小多得多?
Why does std::set container use much more memory than the size of its data?
例如,我们有 10^7 个 32 位整数。将这些整数存储在数组中的内存使用量为 32*10^7/8=40MB。但是,将 10^7 个 32 位整数插入到集合中需要超过 300MB 的内存。法典:
#include <iostream>
#include <set>
int main(int argc, const char * argv[]) {
std::set<int> aa;
for (int i = 0; i < 10000000; i++)
aa.insert(i);
return 0;
}
其他容器如map
,unordered_set
在类似的测试中占用更多的内存。我知道集合是用红黑树实现的,但数据结构本身并不能解释高内存使用率。
我想知道这5~8倍原始数据存储器使用背后的原因,以及一些解决方法/替代方案,以实现更高效的内存设置。
让我们来看看 GCC 中的 std::set 实现(在其他编译器中没有太大区别(。 std::set 在 GCC 上实现为一棵红黑树。每个节点都有一个指向父节点、左节点和右节点的指针以及一个颜色枚举器(_S_red 和 _S_black(。这意味着除了 int(可能是 4 个字节(之外,还有 3 个指针(对于 64 位系统,8 * 3 = 24 个字节(和一个枚举器(因为它在 _Rb_tree_node_base 中的指针之前,它被填充到 8 字节边界,因此实际上它需要额外的 8 个字节(。
到目前为止,我已经为集合中的每个整数计算了 24 + 8 + 4 = 36 个字节。但是由于节点必须对齐到 8 个字节,因此必须填充它,使其可以被 8 整除。这意味着每个节点需要 40 个字节(比 int 大 10 倍(。
但这还不是全部。每个这样的节点都由std::allocator
分配。此分配器使用new
来分配每个节点。由于delete
不知道要释放多少内存,因此每个节点也有一些与堆相关的元数据。元数据至少应该包含分配块的大小,通常需要 8 个字节(理论上,在大多数情况下可以使用某种霍夫曼编码并仅存储 1 个字节,但我从未见过有人这样做(。
考虑到所有因素,每个int
节点的总数为 48 字节。这是int
的 12 倍.集合中的每个int
比数组或向量中花费的多 12 倍。
您的数字表明您使用的是 32 位系统,因为您的数据只需要 300 MB。对于 32 位系统,指针需要 4 个字节。这使得节点中的红黑树相关数据为 3 * 4 + 4 = 16 字节 + int 的 4 + 元数据的 4。每个int
总共有 24 个字节,而不是 4 个字节。这使得它比大集合的矢量大 6 倍。这些数字表明堆元数据需要 8 个字节,而不仅仅是 4 个字节(可能是由于对齐约束(。
因此,在您的系统上,而不是 40MB(如果std::vector
(,预计需要 280MB。
如果你想节省一些花生,你可以为你的套装使用非标准的分配器。您可以通过使用 boost 的隔离存储节点分配器来避免元数据开销。但就记忆而言,这并不是一个很大的胜利。不过,它可以提高您的性能,因为分配器比new
和delete
中的代码更简单。
- 如何实现高效的算法来计算大型数据集的多个不同值?
- 为什么映射插入和 map.find() 的单次迭代比插入和 map.find() 的两次单独迭代慢得多
- 为什么 SDL 在 Mac 上比 Linux 慢得多?
- 将数据发送到多个客户端 UDP 时不支持地址族
- 为什么 std::set 容器使用的内存比其数据大小多得多?
- 如何有效地实现将向量的数据分配给多个变量?
- 为什么我的 Rcpp 代码比 glmnet 慢得多?
- 无法构造具有给定数据类型的多映射
- 在VC2015U3上,std::regex比boost::regex慢得多
- 为什么通过引用返回向量比通过移动返回要快得多?
- 禁用优化后,quick-bench.com 基准测试要快得多
- 使用 scanf() 具有不同数据类型的多个输入
- C++ OpenMP 斐波那契:1 个线程的执行速度比 4 个线程快得多
- Linux 在从文件中读取数据并放入矢量时比 Windows 快得多.txt.我将如何加速Windows以做同样的事情
- Qt:通过深度复制访问列表中的数据结构是否应该比通过指针访问它慢得多
- 为什么使用OpenCL下载数据比在GPU上上传要慢得多
- 反汇编的 exe 文件包含比源代码多得多的代码
- std::shared_ptr 占用的内存空间比 vector 多得多
- 长数据类型计算-为什么Java比c++快得多
- c++ set输出的元素比它包含的元素多得多