【问题标题】:Why does vector pointers takes so much memory?为什么向量指针占用这么多内存?
【发布时间】:2013-02-27 14:37:46
【问题描述】:

我正在做一个项目,我需要在内存使用方面尽可能地刻薄。我正在尝试计算示例中大小为32vector<bool> 的总大小:

vector<bool> v(32);
cout << "sizeof: " << sizeof(v) << endl;
cout << "size: " << v.size() << endl;
cout << "capacity: " << v.capacity() << endl;
cout << "max_size: " << v.max_size() << endl;

这给了我:

sizeof: 40                     <- 40 byte? wtf?
size: 32                       <- hoping an element takes up 1 bit
                                  (instead of the usual 1 byte for booleans)
                                  this should take around 32 bit memory
capacity: 64                   <- I guess this is because minimum size is
                                  1 integer = 64 bit
max_size: 9223372036854775744  <- relevant somehow?

在我的 64 位 ubuntu 12.04 机器上。所以我想我可以这样计算内存:

40 * 8 + 64 * 1 = 384 bit = 48 byte

所以根据这个计算,大部分内存都花在了大小为32 的向量对象上。我的问题是为什么 vector 对象需要使用这么多内存?还有我的计算有什么错误吗?如果不对32 左右的向量大小进行按位操作,我如何才能提高效率?

【问题讨论】:

  • 在您展示的示例中看不到任何指针。
  • vector&lt;bool&gt; 是特例,有特殊处理。
  • 很多误解...'size()==32' 表示你有 32 位,这是正确的。 'capacity()==64' 意味着您可以再添加 32 个,并且可能与 integer 的大小无关(int 在大多数平台上通常为 32 位)。请注意,您所说的 vector pointer 实际上是 vector object
  • @AlokSave 啊对,但我不知道该怎么称呼它。
  • 向量持有多个指针(否则将无法工作!)。

标签: c++ vector stl boolean


【解决方案1】:

这 40 个字节是管理开销。除其他外,vector 必须跟踪其sizecapacity(这已经是size_ts 的两个字节了),非常重要的是,一个指向实际数据的指针!

vector保存的实际数据由默认vector分配器在堆上分配,其内存消耗不包含在sizeof的结果中。

【讨论】:

  • 嗯,有点道理。有什么建议可以提高效率吗?
  • 什么是“低效”?您当然可以有一个以 32 位存储大小的类,将存储的最大元素数限制为 40 亿个,并节省 8 个字节。除非你有很多向量,否则我怀疑这不值得做。
  • @gokcehan 正如我在回答中提到的,如果您知道需要存储的最大位数,则可以改用std::bitset
【解决方案2】:

sizeof(v) 正在获取v 结构的大小,而不是v 数据的大小。这就像在做:

struct S { int* x };

S s;
s.x = new int[10000];

sizeof(s); // only the size of int* (it doesn't/can't check how much data is allocated)

至于为什么std::vector&lt;bool&gt; 可能具有比std::vector&lt;int&gt; 更大的结构大小,请记住bool 版本是专用的。它必须有一些额外的成员来保存按位记录。

【讨论】:

  • 我已经尝试通过 + 64 * 1 将它指向的数据大小添加到总数中
【解决方案3】:

如果您的std::vector 的大小实际上在编译时是固定的,那么改用std::array 可能是有意义的;数组没有任何开销(虽然有填充)。

但是,在bool 的特殊情况下,您应该考虑std::bitsetbitset 通过其大小进行参数化,并将bool 打包为尽可能高效。它的内存效率不如打包数组(比您自己编写的),因为它保留了切换位的计数。

liveworkspace 上的演示:

#include <bitset>
#include <array>
#include <vector>

#include <iostream>

int main() {
   std::vector<bool> vec(32);
   std::array<bool, 32> arr;
   std::array<uint8_t, 4> packed;
   std::bitset<32> bs;

   std::cout << "vector: " << sizeof(vec) + vec.capacity()/8 << "\n";
   std::cout << "array : " << sizeof(arr) << "\n";
   std::cout << "packed: " << sizeof(packed) << "\n";
   std::cout << "bitset: " << sizeof(bs) << "\n";
}

给予:

vector: 48
array : 32
packed: 4
bitset: 8

packed 显然是最高效的容器,但bitset 提供了一个现成的容器,成本相当低(与 64 位机器上的指针一样大)。

【讨论】:

  • 这真的很有帮助,你能详细说明一下packed 吗?它是如何工作的?
  • @gokcehan:它手动工作,想法是你只需要 32 位(所以实际上 uint32_t 就足够了)所以你创建一个 32 位数组(一般来说,ceil(N/CHAR_BITS) ) 然后使用位掩码操作来查询/设置位。显然,编写 get/set 方法需要做一些工作,但是按位操作在网上有很好的记录,所以你不应该有任何麻烦......你最终只是重新实现了bitset(虽然,用较低的以没有 O(1) "count" 方法为代价的内存占用);这是你的决定,你的情况是最好的。
  • 啊,我明白了。我已经在我的代码周围使用了[] 操作,bitset 在我切换到它时并没有抱怨它。我想我可以以某种方式超载[],但这对我来说是相当多的工作。我也意识到我需要c++0x 来存储数组,我最好不要这样,所以我现在会坚持使用这些位集。我会接受你的回答,因为它是最全面的,谢谢..
【解决方案4】:

这就是您为可动态调整大小的结构所付出的代价。 sizeof(v) 返回的实际上只是向量跟踪其存储数据的动态分配内存的开销。该数字不包括用于存储数据的空间。

如果您在编译时知道项目数,则可以改用std::bitsetstd::bitset 实际上将使用您存储的每个布尔值 1 位(四舍五入到它使用的字长),而不会产生 std::vector 的开销。

【讨论】:

  • 我试过了,bitset 只占用 8 个字节。我想这应该是最小值,因为我需要用指针以某种方式指向数据。谢谢,+1。
【解决方案5】:
  • v.size() 返回可以存储在 vector 中的元素数。与内存大小无关
  • v.capacity() 返回为向量分配的内存单元数量。向量会提前分配一些内存,但最初并不是所有的都可以访问。与内存大小无关
  • sizeof(v) 正在获取结构本身的字节大小。我想这里有一些指针。以及大小变量等。

如果您想获取向量元素所占用的内存,这可能是最接近的表达式:v.size() * sizeof(v[0])

【讨论】:

  • 如果已经分配了一些内存,我不应该对此感兴趣吗?
  • @gokcehan 取决于您要测量的内容。一般来说,我的回答是否定的,因为这只是恒定的开销——不管向量中有多少元素,你有 40 个字节的开销
  • 我尝试了几个不同的数字,vector 正如你所说的那样线性增长,而不是通常的向量指数增长。这对我来说有点有趣,因为我觉得它会影响将元素推送到向量的摊销成本的复杂性分析。
  • @gokcehan“向量的指数增长”你到底是什么意思?你做什么测试?
  • 我的意思是容量像1, 2, 4, 8, 16, 32 ..一样增长,当我不断添加元素时,但结果证明我做错了测试,所以vector&lt;bool&gt;容量也像64, 128, 256, 512 ..一样呈指数增长。
【解决方案6】:

查看g++实现的源码可以找到以下成员:

vector 继承自 _Bvector_impl _M_impl;

从继承的_Bvector_impl

_Bit_iterator   _M_start;   16 bytes
_Bit_iterator   _M_finish;  16 bytes
_Bit_type*  _M_end_of_storage;  8 bytes

总计 40 个字节。

_Bit_iterator 包含

_M_offset, an unsigned int: 8 bytes
_M_p,      a pointer 8 bytes. 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-13
    • 2012-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多