嵌套向量在c ++中占用大量内存

时间:2014-03-23 11:58:13

标签: c++ vector nested memory-consumption

我试图找出为什么我的应用程序消耗太多内存。这是:

#include <iostream>
#include <sstream>
#include <string>
#include <exception>
#include <algorithm>
#include <vector>
#include <utility>
#include <assert.h>
#include <limits.h>
#include <time.h>
#include <tchar.h>
#include <random>

typedef unsigned __int32 uint;

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    vector<vector<uint>> arr(65536 * 16, vector<uint>());
    mt19937 mt;
    mt.seed(time(NULL));
    uniform_int<uint> generator(0, arr.size() - 1);
    for (uint i = 0; i < 10000000; i++)
    {
        for (uint j = 0; j < 16; j++)
        {
            uint bucketIndex = generator(mt);
            arr[bucketIndex].push_back(i);
        }
    }

    uint cap = 0;
    for (uint i = 0; i < arr.size(); i++)
    {
        cap += sizeof(uint) * arr[i].capacity() + sizeof(arr[i]);
    }
    cap += sizeof(vector<uint>) * arr.capacity() + sizeof(vector<vector<uint>>);
    cout << "Total bytes: " << cap << endl;
    cout << "Press any key..." << endl;
    cin.get();
}

我使用Windows 7 64位和Visual Studio 2010,代码也编译为64位。

代码在Debug和Release

中输出以下内容

总字节数:914591424

看起来正确(您可以手动检查),但内存管理器显示该应用程序消耗〜 1.4千兆字节的内存

这些500兆字节来自哪里?你能否告诉我如何解决这个问题?

更新

问题是由内存碎片引起的。可以通过不时压缩内存来解决。

3 个答案:

答案 0 :(得分:2)

这是因为每个向量包含三个指针(或它们的道德和大小等价物):begin,begin + size和begin + capacity。因此,当你有一个包含大量其他小向量的向量时,每个内向量会浪费三个字(在64位系统上为24字节)。

由于每个内部向量的begin()指向单独的分配,因此您需要支付N倍的分配开销。那可能是另外几个字节。

相反,您可能希望分配单个大区域并将其视为2D数组。或者使用提供此类功能的众多库中的一个。如果你的内部矢量具有不同的大小,那么这将不起作用,但通常它们都是一个尺寸,所以你真的想要一个2D“矩形”,而不是矢量矢量。

答案 1 :(得分:1)

问题是你不知道数组的确切大小,否则你可以在实际填充之前用reserve设置矢量容量,这样你就可以避免碎片。请尝试以下方法:

  1. 生成随机种子(time(NULL))并保存以供日后使用。
  2. 创建一个数组大小为std::vector<uint>的{​​{1}},并将其中的所有整数/计数器初始化为零,让我们将此数组/向量命名为&#34; vec_sizes&#34;。我们将使用此数组来存储/查找我们稍后将创建/填充的数组的大小。
  3. 使用步骤#1中获取的种子初始化随机生成器。
  4. 运行您的算法(嵌套for循环),但不是将项目存储到2D矢量中,就像代码中的65536 * 16一样,只需增加arr[bucketIndex].push_back(i);计数器。
  5. 现在我们知道所有载体的大小。

    1. 创建vec_sizes[bucketIndex]向量。
    2. 对于arr中的所有子向量,调用向量的arr方法,并在reserve向量中找到相应的大小。这应该有效地预先分配向量,你可以避免重新分配。
    3. 使用我们在步骤#1中存储的相同种子初始化随机生成器。
    4. 运行您的算法。现在将数据推送到向量中并不会重新分配,因为他们的vec_sizes调用已经分配了存储空间。
    5. 在这里,我们利用了一个事实,即你使用伪随机生成器,如果从相同的种子开始运行它,则会给出相同的数字序列。

      注意:通常当内存效率是目标时,解决方案正在进行两次工作:首先计算最终数据的不同维度,然后非常有效地分配空间/&#34;紧凑&#34;然后填写有效分配的存储空间。通常你必须牺牲一些东西。

答案 2 :(得分:1)

我与Boost Container的矢量进行了比较。并添加了shrink_to_fit。区别:

Total bytes: 690331672    // boost::container::vector::shrink_to_fit()
Total bytes: 1120033816   // std::vector

(另请注意,boost容器永远不会在默认构造中动态分配。)

这里是代码(没有太大变化,那里):

#include <iostream>
#include <exception>
#include <algorithm>
#include <vector>
#include <utility>
#include <cassert>
#include <cstdint>
#include <random>

#include <boost/optional.hpp>
#include <boost/container/vector.hpp>

using boost::container::vector;
using boost::optional;

int main()
{
    vector<vector<uint32_t>> arr(1<<20);
    std::mt19937 mt;
    mt.seed(time(NULL));

    std::uniform_int_distribution<uint32_t> generator(0, arr.size() - 1);
    for (uint32_t i = 0; i < 10000000; i++)
    {
        for (uint32_t j = 0; j < 16; j++)
        {
            auto& bucket = arr[generator(mt)];
            //if (!bucket) bucket = vector<uint32_t>();

            bucket.push_back(i);
        }
    }

    for(auto& i : arr)
        i.shrink_to_fit();

    uint32_t cap = 0;
    for (uint32_t i = 0; i < arr.size(); i++)
    {
        cap += sizeof(uint32_t) * arr[i].capacity() + sizeof(arr[i]);
    }
    cap += sizeof(vector<uint32_t>) * arr.capacity() + sizeof(arr);
    std::cout << "Total bytes: " << cap << std::endl;
    std::cout << "Press any key..." << std::endl;
    std::cin.get();
}

更新内存配置文件

--------------------------------------------------------------------------------
Command:            ./test
Massif arguments:   (none)
ms_print arguments: massif.out.4193
--------------------------------------------------------------------------------


    MB
822.7^                                                                      # 
     |                                                                    @@# 
     |                                                                  @@@@#:
     |                                                                @@@@@@#:
     |                                                              @@@@@@@@#:
     |                                                          :@@@@@@@@@@@#:
     |                                                   :::::@@:@@@@@@@@@@@#:
     |                                                 @@:: ::@ :@@@@@@@@@@@#:
     |                                              @@@@@:: ::@ :@@@@@@@@@@@#:
     |                                            @@@ @@@:: ::@ :@@@@@@@@@@@#:
     |                                        :::@@@@ @@@:: ::@ :@@@@@@@@@@@#:
     |                                   @@@:::: @@@@ @@@:: ::@ :@@@@@@@@@@@#:
     |                                @@@@@ :::: @@@@ @@@:: ::@ :@@@@@@@@@@@#:
     |                             @@@@ @@@ :::: @@@@ @@@:: ::@ :@@@@@@@@@@@#:
     |                         @@@:@@@@ @@@ :::: @@@@ @@@:: ::@ :@@@@@@@@@@@#:
     |                     @@@@@@ :@@@@ @@@ :::: @@@@ @@@:: ::@ :@@@@@@@@@@@#:
     |                   @@@@ @@@ :@@@@ @@@ :::: @@@@ @@@:: ::@ :@@@@@@@@@@@#:
     |               @@@@@@@@ @@@ :@@@@ @@@ :::: @@@@ @@@:: ::@ :@@@@@@@@@@@#:
     |        :::::::@@ @@@@@ @@@ :@@@@ @@@ :::: @@@@ @@@:: ::@ :@@@@@@@@@@@#:
     | ::@:@:::: ::: @@ @@@@@ @@@ :@@@@ @@@ :::: @@@@ @@@:: ::@ :@@@@@@@@@@@#:
   0 +----------------------------------------------------------------------->Gi
     0                                                                   69.85

enter image description here

enter image description here