在短期运行的程序中释放内存的最重要部分是促进内存重用。您的处理器的缓存有限,可能只有几 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 亿。