C++ 使用向量加载 500MB 数据使用 5GB RAM
C++ loading 500MB of data with vector use 5GB of RAM
我正在编写用于对大块字符串(~ 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。 (OS:Windows7 64 位,IDE:Jetbrains CLion,编译器:G++)
正常吗?我不明白为什么它只使用这些大量的 ram 来获取数据。我还通过添加另一个循环检查了输出,它们是正确的。所以没有无限循环或类似的东西。代码运行良好,但使用大量 RAM。
想知道为什么它会使用这么大的 RAM。
是什么让您认为您消耗了 5Gb?
你创建了一个 46657 的数组vector<char*>
。每个向量平均有 100 char*
指向一个新分配的 255 字节的字符串。那至少是 sizeof(buckets)+46657*100*(sizeof(char*)+255)
字节。根据实施情况,这可能约为 1.2 Gb。向量可能保留一些 space 以用于更快的增长。但这不会从根本上改变我们的量级。
所有这些都很大,可能超出您的需要,但与您测量的 5Gb 相去甚远。但是你首先测量了什么?
您提供的内存消耗统计数据很可能在操作系统级别进行管理。它是执行代码的进程消耗的内存,不一定是代码消耗的代码。所有这些都依赖于实现,但通常标准库可能会从 OS 分配非常大的内存块,因为对 OS 的调用比用户 space 中的本地调用更昂贵。然后在每次 new
或 malloc
调用时将这个大块切成小块。就像批发商和零售商。
要知道你的代码消耗的内存,你需要做更精确的内存monitoring/profiling。例如,您可以使用 valgrind
或其他工具,具体取决于您的 OS。
避免泄漏
我们没有看到完整的代码,所以也不排除泄露的可能。由于您手动管理memroy,因此泄漏的风险更高。更不用说未经清理的扫描,它可能会超过分配的 255 个字符并损坏内存。
因此,更安全的方法可能是:
vector<string> buckets[46657];
...
for ...
string temp; // let the string take care of its own memory
getline(cin, temp);
...
作为副作用,如果您有许多较小的字符串,您还可以从优化的内存管理中受益。
我正在编写用于对大块字符串(~ 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。 (OS:Windows7 64 位,IDE:Jetbrains CLion,编译器:G++)
正常吗?我不明白为什么它只使用这些大量的 ram 来获取数据。我还通过添加另一个循环检查了输出,它们是正确的。所以没有无限循环或类似的东西。代码运行良好,但使用大量 RAM。
想知道为什么它会使用这么大的 RAM。
是什么让您认为您消耗了 5Gb?
你创建了一个 46657 的数组vector<char*>
。每个向量平均有 100 char*
指向一个新分配的 255 字节的字符串。那至少是 sizeof(buckets)+46657*100*(sizeof(char*)+255)
字节。根据实施情况,这可能约为 1.2 Gb。向量可能保留一些 space 以用于更快的增长。但这不会从根本上改变我们的量级。
所有这些都很大,可能超出您的需要,但与您测量的 5Gb 相去甚远。但是你首先测量了什么?
您提供的内存消耗统计数据很可能在操作系统级别进行管理。它是执行代码的进程消耗的内存,不一定是代码消耗的代码。所有这些都依赖于实现,但通常标准库可能会从 OS 分配非常大的内存块,因为对 OS 的调用比用户 space 中的本地调用更昂贵。然后在每次 new
或 malloc
调用时将这个大块切成小块。就像批发商和零售商。
要知道你的代码消耗的内存,你需要做更精确的内存monitoring/profiling。例如,您可以使用 valgrind
或其他工具,具体取决于您的 OS。
避免泄漏
我们没有看到完整的代码,所以也不排除泄露的可能。由于您手动管理memroy,因此泄漏的风险更高。更不用说未经清理的扫描,它可能会超过分配的 255 个字符并损坏内存。
因此,更安全的方法可能是:
vector<string> buckets[46657];
...
for ...
string temp; // let the string take care of its own memory
getline(cin, temp);
...
作为副作用,如果您有许多较小的字符串,您还可以从优化的内存管理中受益。