【问题标题】:Do you think its a good idea not to deallocate memory for small programs? [closed]你认为不为小程序释放内存是个好主意吗? [关闭]
【发布时间】:2014-06-03 16:58:13
【问题描述】:

假设我的系统有 4GB 内存,某个程序在整个执行过程中只消耗 100MB 内存,并且运行时间有限,比如只有 30 秒。您不认为在执行该程序期间根本不释放内存(通过提高性能)是个好主意吗?无论如何,这 100 MB 将在该程序终止时被释放。

【问题讨论】:

  • “好主意”是什么意思?它会起作用,但我认为不解除分配没有任何好处,而我确实看到了养成坏习惯的坏处。
  • 如果您希望您的代码成为不可维护、不可重用的混乱,并且如果您讨厌您的读者,那当然可以。当然,如果你没有成长为程序员和人类的野心。
  • 这听起来很像旧的“好吧,我将使用冒泡排序,因为我只有 20 个元素”逻辑......今天有一个目的的代码可能会发现自己在未来被不同地使用。健壮总是一件好事。
  • 如果你想避免动态内存释放带来的性能损失,你应该首先避免动态内存分配。
  • “提高性能”多少?如果它在“有限的时间内”运行,你能说清楚吗?假设你削减,说运行时间减少 5 毫秒。你需要运行多少次才能弥补你问这个问题和其他人阅读它所花费的时间?

标签: c++ c linux


【解决方案1】:

在短期运行的程序中释放内存的最重要部分是促进内存重用。您的处理器的缓存有限,可能只有几 MB,如果您不断分配更多内存,您将永远无法利用它。

但是,如果您释放了一些内存,然后重新分配并重用它,则很有可能在重用时内存已经“热”并驻留在缓存中。这意味着您不会因缓存未命中而受到惩罚,并且您的程序将运行得更快。

当然,您要权衡缓存未命中的释放/重新分配成本,但不断分配越来越多的内存肯定会导致最低级别的缓存未命中。在现代处理器上,这些缓存未命中中的每一个都会花费大约 100 条指令,远低于分配/解除分配的成本。

编辑

所以我做了一个小测试程序来测试这个。

#include <stdio.h>
#include <stdlib.h>

process1(){
    char * ptr = malloc(1 << 20);
    int i = 0;
    while (i < (1<<20)) ptr[i++] = rand();
    free(ptr);
}

process2(){
    char * ptr = malloc(1 << 20);
    int i = 0;
    while (i < (1<<20)) ptr[i++] = rand();
}


main(){
    int i = 100;
    while(i--) process1();
    i = 100;
    while(i--) process2();
}

两个进程都会处理 100 MB 的数据,第一个进程会在执行过程中释放它,而第二个进程不会。我使用 valgrind 的 cachegrind 工具对此进行了分析。这是结果

fn=process1
0 943720200 2 2 419430900 100 0 209715800 1638400 32767
fn=process2
0 943719900 0 0 419430800 100 0 209715700 1638400 1605784

好吧,不那么令人兴奋,我会翻译。通过避免空闲,您节省了不到 0.1% 的指令周期,并且您招致了大约一百万次 LL 缓存未命中。如果我们使用标准循环估计公式 CEst = lr + 10 Llm + 100 LLm ,那么仅避免函数中的 free 会导致性能下降约 16%。

我在 callgrind 中重新运行以获得完整的故事(我不会发布详细的结果,因为它们比缓存研磨的结果要复杂得多)但是当我们包含完整的调用以释放时,结果是相同的,a当您不免费通话时,使用的周期会增加 16%。

您可以随意在您自己的程序上进行相同的测试,但在这种情况下,结果很清楚。与使用新内存不断刷新缓存的成本相比,管理动态内存的成本确实微不足道。

另外,我没有提到这一点,但应该直接比较的是免费成本与缓存未命中成本。 frees 总共花费了 38930 个周期,缓存未命中 157332000 个,您节省了 39000 个周期,并为此付出了 1.5 亿。

【讨论】:

  • 好吧,如果你不重用部分内存,无论如何它都会从缓存中消失,所以我看不到那里的问题。但是你的第二点很有趣,在相同的内存上重新分配确实可以增加缓存命中,从而减少缓存未命中。
  • @MetallicPriest 不重用内存是不好的,因为从缓存中弹出该内存并用其他内存替换它需要花费周期。这就是整个问题。
  • 这些是大内存分配。用小内存分配检查这一点会很有趣。
  • @MetallicPriest 小内存分配意味着您可能也会在一级缓存未命中时咀嚼。但就像我说的测试你自己的代码。如果释放真的那么昂贵,考虑使用不同的分配器之前你考虑完全忽略释放
【解决方案2】:

Don't you think its a good idea not to deallocate memory at all during the execution of that program?

偷工减料不是一个“好主意”。我可以想到很多释放内存的理由;我想不出这么多来分配未使用的内存。如果您不想处理内存问题,请不要使用没有自动垃圾收集功能的语言进行编写,或者,由于您使用 C++ 编写,请使用 smart pointers

only consumes 100 MB of memory

这是我见过的对“唯一”一词最严重的误用。 100 MB 是一个很大的变化。这大约是您正在使用的内存的 1/40,即使系统上运行的 40 个其他程序也以相同的视角进行编程,充其量您会让系统因为交换延迟而停止运行,并且在最糟糕的是,您会遇到完整的系统崩溃。通常,在任何给定的(现代)机器上运行的应用程序超过 40 个。

also runs for a limited time, like only 30 seconds

同样,30 秒对于计算机来说是很长的时间。但是,澄清一下,您的意思是 ever 30 秒,还是每天 30 秒?因为如果这是一次性的并且你的时间很短,也许这是一个可以接受的损失,但这肯定不是一个好习惯。

TL;DR?

如果您有更重要、更紧迫的考虑(例如,产品截止日期即将到来)并且您没有其他合理的选择,那就去做吧。否则,你没有任何借口。占用记忆永远不是你应该感觉良好的事情。它会严重减慢真正确实需要该内存的进程。是的,O/S 会在程序结束后清理资源。不,这并不能让您全权委托您编写滥用其资源的程序。

【讨论】:

    【解决方案3】:

    不,这不是好的编程风格。几乎所有现代操作系统都会在之后清理内存,但只要程序运行,它就会一直泄漏。

    【讨论】:

    • 但它能否通过消除释放的开销来提高应用程序的性能?
    • @MetallicPriest 取决于,如果您可以将 decallocation 推迟到程序结束,它应该是相同的。如我所见,要么是您进行批量清理,要么是操作系统执行此操作:)
    • 我真的不认为相信你无法控制的东西为你做某事是个好主意。
    • @KrisMorness 什么?你不相信操作系统会为你管理内存?
    【解决方案4】:

    如果您想避免清理带来的开销,您可以实现一个分配器,在保持良好做法的同时允许这样做。例如,在程序启动时分配内存池,将全局 operator new 替换为从池中获取内存的版本,并将全局 operator delete 替换为无操作。然后在程序退出前释放内存池。您还可以使用更多查找粒度池来减少释放开销,但仍允许程序运行而不会持续增加内存使用量。

    另一种选择是简单地使用new/delete 的更快实现。内置版本传统上速度很慢,但并非必须如此。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-27
      • 1970-01-01
      • 2019-05-15
      • 1970-01-01
      • 1970-01-01
      • 2020-05-28
      • 2018-11-18
      • 2011-09-25
      相关资源
      最近更新 更多