减少利用大矢量的 c++ 程序的内存占用

Reducing memory footprint of c++ program utilising large vectors

本文关键字:c++ 程序 内存      更新时间:2023-10-16

在扩大我交给一个自编码程序的问题大小时,我开始遇到Linux的OOM杀手锏。Valgrind(在CPU上运行时(和cuda-memcheck(在GPU上运行时(都不会报告任何内存泄漏。在迭代内部循环时,内存使用量不断扩大,而我显式清除了在此循环结束时保存最大数据块的向量。如何确保这种内存占用会消失?

执行了内存泄漏检查,所有内存泄漏都已修复。尽管如此,内存不足错误仍然会杀死程序(通过 OOM Killer(。手动监视内存消耗显示内存利用率增加,即使在显式清除包含数据的向量后也是如此。

要知道的关键是有三个嵌套循环,一个外部包含手头的子问题。中间循环在蒙特卡洛试验上循环,内部循环运行试验内所需的一些顺序过程。伪代码如下所示:

std::vector<object*> sub_problems;
sub_problems.push_back(retrieved_subproblem_from_database);
for(int sub_problem_index = 0; sub_problem_index < sub_problems.size(); ++sub_problem_index){
std::vector< std::vector<float> > mc_results(100000, std::vector<float>(5, 0.0));
for(int mc_trial = 0; mc_trial < 100000; ++mc_trial){
for(int sequential_process_index = 0; sequential_process_index < 5; ++sequential_process_index){
mc_results[mc_trial][sequential_process_index] = specific_result;
}
}
sub_problems[sub_problem_index]->storeResultsInObject(mc_results);
// Do some other things
sub_problems[sub_problem_index]->deleteMCResults();
}

删除MCResults如下所示:

bool deleteMCResults() {
for (int i = 0; i < asset_values.size(); ++i){
object_mc_results[i].clear();
object_mc_results[i].shrink_to_fit();
}
object_mc_results.clear();
object_mc_results.shrink_to_fit();
return true;
}

如何确保内存消耗完全依赖于中间循环和内部循环而不是外部循环?从理论上讲,第二个、第三个和第四个等可以使用与第一次迭代完全相同的内存空间/地址。

也许我从字面上阅读了您的伪代码,但看起来您有两个mc_results变量,一个在for循环中声明,另一个是deleteMCResults正在访问的。

无论如何,对于如何调试它,我有两个建议。 首先,与其让 OOM 杀手级攻击(这需要很长时间、不可预测并且可能会杀死重要的东西(,不如使用ulimit -v来限制进程大小。 将其设置为合理的值,例如 1000000(约 1GB(,并努力将您的进程保持在该范围内。

其次,开始删除或注释掉除程序中分配和释放内存的部分之外的所有内容。 要么你会找到你的罪魁祸首,要么你会制作一个足够小的程序来完整地发布。

deleteMCResults()

可以写得简单得多。

void deleteMCResults() {
decltype(object_mc_results) empty;
std::swap(object_mc_results, empty);
}

但在这种情况下,我想知道你是否真的想释放内存。正如您所说,迭代可以重复使用相同的内存,因此也许您应该将deleteMCResults()替换为returnMCResultsMemory().然后将mc_results声明提升到循环之外,并在returnMCResultsMemory()返回后将其值重置为 5.0。

有一件事可以很容易地从你显示的代码中改进。但是,它确实不够充分,也不够精确,无法进行全面分析。提取相关示例([mcve](并可能要求对 codereview.stackexchange.com 进行审查可能会改善结果。

可以做的简单事情是用五个浮点数的数组替换五个浮点数的内部向量。每个向量(在典型实现中(由三个指针组成,指向分配内存的开始和结束,另一个指针用于标记使用量。实际存储需要单独的分配,这反过来又会产生一些开销(以及访问数据时的性能开销,关键字"引用位置"(。这三个指针在普通的 64 位计算机上需要 24 个八位字节。与五个浮点数相比,这些浮点数只需要 20 个八位字节。即使这些浮点数被填充到 24 个八位字节,您仍然可以从省略单独的分配中受益。

为了尝试这个,只需用std::array(https://en.cppreference.com/w/cpp/container/array(替换内部向量。很有可能您不必更改太多代码、原始数组、std::arraystd::vector具有非常相似的接口。