【问题标题】:C++ loading 500MB of data with vector use 5GB of RAMC++ 使用向量加载 500MB 数据使用 5GB RAM
【发布时间】:2020-01-28 21:18:38
【问题描述】:

我正在编写一个用于对一大块字符串(~ 2GB)进行排序的代码,并且我正在使用类似于桶排序的方法。我有大约 47000 个向量,每个向量平均有大约 100 个元素(char*)。 (由于输入,有些可能有很多元素,有些可能是空的)

我获取输入的代码是这样的:

#include <iostream>
#include <string>
#include <vector>
#include <cstring>
#define digitize(a) ((a>47 && a<58)*(a-48)+(a>96 && a<123)*(a-87)) //base 36 convert
int main(){
 int n = 0;
    scanf("%d\n", &n);

    vector<char *> buckets[46657]; // 36 *36 *36 = 46656




    for (int i = 0; i < n; i++) {
        char *temp = static_cast<char *>(calloc(255, sizeof(char)));
        scanf("%s\n", temp);
        int d1 = temp[0];
        int d2 = temp[1];
        int d3 = temp[2];

        int index = digitize(d1) * 1296 + digitize(d2) * 36 + digitize(d3); // 1296 = 36*36

        buckets[index].push_back(temp);

    }
}

digitize 实际上是一个 base 36 转换器。 (即 0:0, 1:1 .... a:10, b:11, ... , z:36)因为我的数据由数字和小写字符组成。

通过在 500MB 数据集(随机生成)上运行此代码,文件的 ram 使用量超过 4GB 并接近 5GB。 (操作系统:Windows7 64bit,IDE:Jetbrains CLion,编译器:G++)

正常吗?我不明白为什么它使用这些大量的内存来获取数据。我还通过添加另一个循环来检查输出,它们是正确的。所以没有无限循环或类似的东西。代码运行良好,但使用了大量 RAM。

知道为什么它会使用如此大量的 RAM。

【问题讨论】:

  • 为什么只使用其中的 3 个字符,却要分配 255 个字符的空间?
  • @NathanOliver 稍后将用于排序。正如我所说,它只是一个非常庞大的程序的输入部分。我把所有部分都注释掉了,发现它的输入部分使用了 5 GB 的 RAM。输入的每个字符串最多可以有 254 个字符。
  • 这个数据集的n 是什么?
  • @hegel5000 应从输入中读取的字符串计数。例如输入可以是:3 abb bbb ccc(实际上字符串比abb大得多,上面只是一个例子)
  • 是的,但是对于您测试的数据集,n 是什么?例如,在这个特定的测试中,您发现程序使用了 ~4-5GB,n 的解析值是什么?

标签: c++ vector memory-management memory-leaks ram


【解决方案1】:

是什么让你觉得你消耗了 5Gb?

您创建了一个包含 46657 vector&lt;char*&gt; 的数组。每个向量平均有 100 个char* 指向一个新分配的 255 字节字符串。那至少是sizeof(buckets)+46657*100*(sizeof(char*)+255) 字节。根据实施情况,这可能约为 1.2 Gb。向量可能会保留一些空间,以便更快地增长。但这不会从根本上改变我们的数量级。

所有这些都是巨大的,可能超出您的需要,但与您测量的 5Gb 相差甚远。但是你首先测量了什么?

您提供的内存消耗统计很可能在操作系统级别进行管理。它是执行代码的进程消耗的内存,不一定是代码消耗的代码。所有这些都取决于实现,但通常标准库可能会从操作系统分配非常大的内存块,因为对操作系统的调用比用户空间中的本地调用更昂贵。然后在每次newmalloc 调用时将这个大块切成小块。这就像批发商和零售商。

要了解您的代码消耗的内存,您需要进行更精确的内存监控/分析。例如,您可以使用valgrind 或其他工具,具体取决于您的操作系统。

避免泄漏

我们没有看到完整的代码,因此也不排除泄漏。由于您手动管理内存,因此泄漏的风险更高。不是说未经清理的扫描,它可能超过 255 个分配的字符和损坏的内存。

因此,更安全的方法可能是:

vector<string> buckets[46657]; 
...
for ...
    string temp; // let the string take care of its own memory
    getline(cin, temp);  
    ...

作为副作用,如果您有许多较小的字符串,您还可以从优化的内存管理中受益。

【讨论】:

    猜你喜欢
    • 2011-04-29
    • 2016-07-04
    • 2022-01-03
    • 1970-01-01
    • 2015-04-25
    • 2015-08-18
    • 1970-01-01
    • 2013-04-22
    • 1970-01-01
    相关资源
    最近更新 更多