以2的幂分配内存是否更好?

时间:2010-07-06 20:53:53

标签: c memory-management malloc

当我们使用malloc()分配内存时,我们应该给出2的幂大小吗?或者我们只是给出我们需要的确切尺寸?

//char *ptr= malloc( 200 ); 
char *ptr= malloc( 256 );//instead of 200 we use 256

如果给出2的幂大小更好,那是什么原因?为什么会更好?

由于

修改

我混淆的原因是关注Joel的博客Back to Basics

  

智能程序员最小化   马克思的潜在破坏   总是分配内存块   这是2的大小。你   知道,4个字节,8个字节,16个字节,   18446744073709551616字节等   原因应该是直观的   任何和乐高一起玩的人,这个   最大限度地减少怪异的数量   在自由中发生的碎片化   链。虽然看起来像这样   浪费空间,也很容易看到   它是如何永远不会浪费超过50%的   空间。所以你的程序使用否   内存的两倍多   需要,这不是那么大   交易。

对不起,我应该早点发布上面的报价。我道歉!

到目前为止,大多数回复都说,以2的力量分配内存是一个坏主意,那么在哪种情况下更好地遵循Joel关于malloc()的观点?他为什么这么说?以上引用的建议现在已经过时了吗?

请解释一下。
感谢

11 个答案:

答案 0 :(得分:54)

只需提供您需要的确切尺寸即可。两个幂的大小可能“更好”的唯一原因是允许更快的分配和/​​或避免内存碎片。

但是,任何关注高效的非平凡malloc实现都会以适当的方式在内部以这种方式对分配进行补偿。你不需要关心“帮助”malloc; malloc可以自己做得很好。

修改

在回应你对Joel on Software文章的引用时,Joel在该部分中的观点(如果没有你引用的段落之后的上下文很难正确辨别),如果你期望经常 re - 分配一个缓冲区,最好是乘法,而不是加法。事实上,这正是C ++中的std::stringstd::vector类(以及其他类)所做的。

这是一个改进的原因并不是因为您通过提供方便的数字来帮助malloc,而是因为内存分配是昂贵的操作,并且您正在尝试最小化你这样做的次数。乔尔提出了一个时空权衡概念的具体例子。他认为,在需要大量内存动态变化的许多情况下,最好浪费一些空间(通过在每次扩展时分配最多两倍的空间)以节省时间每次需要n个字节时,需要重复确定n个字节的内存。

乘数不必是两个:你可以分配最多三倍的空间,最后得到三个幂的分配,或者分配多达五十七倍的空间。并以五十七的权力结束分配。您执行的分配越多,重新分配所需的频率就越低,但浪费的内存就越多。分配两个权力,最多使用两倍的内存,恰好是一个很好的起点权衡,除非你更好地了解你的需求是什么。

他确实顺便提一下,这有助于减少“自由链中的碎片化”,但其原因更多是因为分配的数量和一致性,而不是它们的确切大小。首先,分配和释放内存的次数越多,分割堆的可能性就越大,无论分配的大小是多少。其次,如果你有多个缓冲区,你使用相同的乘法调整大小算法动态调整大小,那么很可能如果一个调整大小从32调整到64,另一个调整大小从16调整到32,那么第二个重新分配可能适合第一个以前是。如果将一个大小调整为25到60,另一个调整大小从16到26,则不会出现这种情况。

同样,如果你只打算进行一次分配步骤,那么他所说的都不适用。

答案 1 :(得分:18)

只是扮演魔鬼的拥护者,这就是Qt如何做到的:

  

我们假设我们追加15000   字符串到QString字符串。然后   以下18个重新分配(出自   QString时可能出现15000)   空间不足:4,8,12,16,20,   52,116,244,500,11012,2036,4084,   6132,8180,10228,12276,14324,   最后,QString分配了16372个Unicode字符,   其中15000人被占用。

     

上面的值可能看起来有点   奇怪,但这里是指导   原则:

     

QString在a处分配4个字符   到达20号的时间。从20点开始   到4084年,它通过加倍而前进   每次大小。更确切地说,它   进入下一个2的力量,   减去12.(一些内存分配器   请求完全时执行最差   两个人的力量,因为他们使用了一些   每个块的字节数用于记账。)   从4084年开始,它逐渐增加   2048个字符(4096个字节)。这个   有道理,因为现代经营   系统不会复制整个数据   重新分配缓冲区时;该   物理内存页面很简单   重新排序,只有数据   实际需要的第一页和最后一页   被复制。

我喜欢他们预期代码中的操作系统功能的方式,这些功能意味着从智能手机到服务器群的性能良好。鉴于他们比我更聪明,我认为所有现代操作系统都可以使用所述功能。

答案 2 :(得分:6)

可能曾经是真实的,但肯定不会更好。

只需在需要时分配所需的内存,并在完成后立即释放。

有太多的程序浪费资源 - 不要让你的资源成为其中之一。

答案 3 :(得分:4)

这有点无关紧要。

Malloc实际上分配的内存比您请求的内存略多,因为它有自己的头文件来处理。因此,最佳存储可能类似于4k-12字节......但这取决于实现。

在任何情况下,您都没有理由将更多的存储空间作为优化技术。

答案 4 :(得分:2)

可能想要根据处理器的字大小分配内存;没有任何旧的权力会做。

如果处理器有32位字(4字节),则以4字节为单位进行分配。以2个字节分配可能没有用,因为处理器更喜欢数据以4字节边界开始。

另一方面,这可能是微优化。大多数内存分配库都设置为返回在正确位置对齐的内存,并且将留下最少量的碎片。如果分配15个字节,则库可以填充并分配16个字节。某些内存分配器根据分配大小具有不同的池。

总之,分配您需要的内存量。让分配库/经理为您处理实际金额。除了担心这些微不足道的问题之外,还要投入更多的精力来确保正确性和稳健性。

答案 5 :(得分:2)

当我分配一个可能需要继续增长的缓冲区来容纳尚未知大小的数据时,我开始使用2减1的幂,并且每次空间不足时,我重新分配两次之前的大小加1.这使得我永远不必担心整数溢出;当前一个大小为SIZE_MAX时,大小只能溢出,此时分配已经失败,无论如何都是2*SIZE_MAX+1 == SIZE_MAX

相比之下,如果我只使用2的幂并且每次加倍,我可能会成功获得一个2 ^ 31字节的缓冲区,然后在下一次将大小加倍时重新分配到0字节缓冲区。

正如有些人评论2-power-12的功率对于某些malloc实现是好的,人们可以同样从2减去12的功率开始,然后加倍并且每步增加12 ...... / p>

另一方面,如果您只是分配不需要增长的小缓冲区,请求您需要的大小。不要试图猜测对malloc有什么好处。

答案 6 :(得分:1)

这完全取决于libc的给定malloc(3)实现。这个实现可以按照它认为合适的顺序保留堆块。

要回答这个问题 - 不,这不是“更好”(这里的“更好”是指......?)。如果您要求的尺寸太小,malloc(3)将在内部保留更大的块,所以只需坚持您的确切尺寸。

答案 7 :(得分:1)

凭借今天的内存量及其速度,我认为它不再具有相关性。

此外,如果您要经常分配内存,最好考虑自定义内存池/预分配。

答案 8 :(得分:1)

总有测试...

您可以尝试在循环中分配内存的“示例”程序。这样你就可以看到你的编译器是否神奇地以2的幂分配内存。 有了这些信息,您可以尝试使用2种策略分配相同数量的总内存:随机大小的块和2个大小的块的功能。

我只会想到大量内存的差异(如果有的话)。

答案 9 :(得分:0)

如果您要分配某种可扩展缓冲区,您需要为初始分配选择一些数字,那么是,2的幂是可供选择的好数字。如果需要为struct foo分配内存,那么只需要malloc(sizeof(struct foo))。 2次幂分配的建议源于内部碎片的低效率,但是用于多处理器系统的现代malloc实现开始使用CPU本地池来进行足够小的分配以解决这个问题,这可以防止以前的锁争用当多个线程同时尝试malloc时会产生结果,并且由于碎片而花费更多时间。

通过仅分配您需要的内容,可以确保数据结构在内存中更密集地打包,从而提高缓存命中率,这对内部碎片的性能影响要大得多。存在非常旧的malloc实现和非常高端的多处理器系统的情况,其中显式填充分配可以提供加速,但是在这种情况下您的资源将更好地用于在该系统上启动和运行更好的malloc实现。预填充还会降低代码的可移植性,并阻止用户或系统在运行时以编程方式或使用环境变量选择malloc行为。

过早优化是万恶之源。

答案 10 :(得分:0)

重新分配时应该使用realloc()而不是malloc()。 http://www.cplusplus.com/reference/clibrary/cstdlib/realloc/

总是使用2的幂?这取决于你的程序在做什么。如果你需要重新处理整个数据结构,当它增长到2的幂时,是的,这是有道理的。否则,只需分配你需要的东西,不要占用内存。