更快地 malloc 多次小时间或几次大时间?
Posted
技术标签:
【中文标题】更快地 malloc 多次小时间或几次大时间?【英文标题】:Faster to malloc multiple small times or few large times? 【发布时间】:2009-07-07 19:14:51 【问题描述】:当使用 malloc 分配内存时,通常是对较小数据块执行多个 malloc 还是对较大数据块执行较少 malloc 更快?例如,假设您正在处理具有黑色像素和白色像素的图像文件。您正在遍历像素并希望将每个黑色像素的 x 和 y 位置保存在一个新结构中,该结构还具有指向下一个和前一个像素 x 和 y 值的指针。使用指针遍历像素为每个黑色像素的 x 和 y 值分配一个新结构通常会更快,还是通过迭代一次来获得黑色像素的数量会更快,然后分配一个大的使用仅包含 x 和 y 值但没有指针的结构的内存块,然后再次迭代,将 x 和 y 值保存到该数组中?我假设某些平台可能与其他平台不同,哪个平台更快,但每个人都认为什么平台通常会更快?
【问题讨论】:
【参考方案1】:这取决于:
多次小次表示多次,比较慢 对于小分配可能有特殊/快速的实现。如果我在乎,我会测量它!如果我真的很在乎,并且无法猜测,那么我可能会同时实现两者,并在目标机器上运行时进行测量,并相应地进行调整。
一般来说,我认为越少越好:但是存在大小和运行时库实现,这样(足够)大的分配将被委托给(相对较慢的)O/S。而(足够)小的分配将从(相对快速的)已经分配的堆中提供。
【讨论】:
你怎么知道——一般来说——你的系统有这样一个奇迹库? 引用 ChrisW 的话:“如果我在乎,我会测量它!” @all+author:我只是很好奇有人在这里提出问题并接受某人的回答,他自己告诉我们,他不在乎(迪奥纳达尔引用他的话)。我知道,我们在这里不是在做科学,但是为什么在这种情况下问呢? @Juergen 我对 OP 说的,也许是以一种笨拙的方式,如果 IMO 关心,那么 他 应该测试它。猜测是可能的(甚至可能很有趣),但实际答案是特定于平台的。 @ChrisW:当他问起时,我不认为 OP 想要测量它。【参考方案2】:分配大块更高效;此外,由于您使用的是更大的连续块,因此您具有更大的引用局部性,并且在生成内存结构后遍历它也应该更有效!此外,分配大块应该有助于减少内存碎片。
【讨论】:
请注意,如果/当您释放它们时,较大的块会导致更严重的碎片化。 @Javier:一般来说,释放一个由较小块组成的较大块在碎片化方面比由他们自己分配/释放那些较小的块更好。我不能用 500 多个字符来证明它,但你也不能证明你的大胆声明。【参考方案3】:一般来说,分配大块内存的次数越少,速度越快。每次调用 malloc() 都会产生开销。
【讨论】:
有关更多信息,请查看 Bonwick 的有关平板分配的 Usenix 论文。 usenix.org/publications/library/proceedings/bos94/full_papers/…【参考方案4】:除了速度问题还有memory fragmentation problem。
【讨论】:
【参考方案5】:分配内存是工作。分配内存块时完成的工作量通常与块的大小无关。你从这里开始。
【讨论】:
@ovanes:据我所知,你所说的与尼尔相反。你也不要试图打败编译器,而是一个库例程。所以你的观点在这里是错误的。当您的分配问题如此复杂时,您无法击败此例程(我从问题中看不到这一点),那么您就有麻烦了,是的!或者你应该学习一些书。【参考方案6】:完全不在性能敏感的代码中分配会更快。提前一次分配你需要的内存,然后尽可能多地使用和重用它。
内存分配一般来说是一个相对较慢的操作,所以不要经常这样做。
【讨论】:
【参考方案7】:通常 malloc 很昂贵。它必须找到一个合适的内存块来分配内存并跟踪不连续的内存块。在几个库中,您会发现小型内存分配器,它们试图通过分配大块并管理分配器中的内存来尽量减少影响。
Alexandrescu 处理“现代 C++ 设计”和 Loki 库中的问题,如果您想看看这样的库。
【讨论】:
【参考方案8】:这个问题恐怕是实用主义问题之一;也就是说,这取决于。
如果您有很多像素,其中只有少数是黑色的,那么计算它们可能是最高的成本。
如果您使用的是 C++,您的标签建议您使用 C++,我强烈建议您使用 STL,例如 std::vector。
如果我没记错的话,vector 的实现使用了一种实用的分配方法。分配策略有一些启发式方法,其中一个信息丰富的是:
class SampleVector
int N,used,*data;
public:
SampleVector() N=1;used=0;data=malloc(N);
void push_back(int i)
if (used>=N)
// handle reallocation
N*=2;
data=realloc(data,N);
data[used++]=i;
;
在这种情况下,您每次重新分配时分配的内存量会增加一倍。 这意味着重新分配的频率逐渐减半。
您的 STL 实现将经过良好调整,所以如果您可以使用它,那就去做吧!
【讨论】:
我不同意。无论如何,您都会遍历像素 - 不要尝试保存计算要存储的像素的额外迭代。在幕后重新分配和复制 std::vector 需要更多时间。 当然,它仍然是您存储多少像素与您必须迭代多少像素的函数。【参考方案9】:要考虑的另一点是它如何与线程交互。在线程并发应用程序中多次使用 malloc 是对性能的主要拖累。在那种环境中,最好使用可扩展的分配器,例如英特尔的Thread Building Blocks 或Hoard 中使用的分配器。 malloc 的主要限制是所有线程都在争夺一个全局锁。添加另一个线程会大大降低您的应用程序的速度,这可能会非常糟糕。
【讨论】:
【参考方案10】:正如已经提到的,malloc 的成本很高,所以更少可能会更快。 此外,在大多数平台上使用像素会减少缓存未命中并且速度会更快。 但是,不能保证在每个平台上都适用
【讨论】:
【参考方案11】:除了分配开销本身,分配多个小块可能会导致大量缓存未命中,而如果您可以遍历连续块,机会会更好。
你描述的场景要求预先分配一个大块,恕我直言。
【讨论】:
【参考方案12】:虽然为每个字节分配的内存分配大块更快,但如果您人为地增加分配大小只是为了自己切碎它,它可能不会更快。您只是在复制内存管理。
【讨论】:
【参考方案13】:对像素进行迭代以计算要存储的像素数。 然后为确切数量的项目分配一个数组。这是最有效的解决方案。
您可以使用 std::vector 来简化内存管理(请参阅 std::vector::reserve 过程)。注意:reserve 可能会分配一点(可能多达 2 倍)更多的内存然后需要。
【讨论】:
【参考方案14】:“我可以全部分配”(真的,我可以!)
我们可以对一些特殊的实现进行哲学化,这些实现大大加快了小分配的速度……是的!但总的来说,这是成立的:
malloc 必须是通用的。它必须实现所有不同类型的分配。这就是它相当慢的原因!可能是,您使用了一个特殊的 kinky-super-duper 库,它可以加快速度,但也不能创造奇迹,因为它们必须全面实施 malloc。
规则是,当您有更专业的分配编码时,您总是比广泛的“我可以全部分配”例程“malloc”更快。
因此,当您能够在编码中以更大的块分配内存时(并且不会花费太多),您可以大大加快速度。此外 - 正如其他人所提到的 - 您将获得更少的内存碎片,这也可以加快速度并且可以减少内存成本。您还必须看到,malloc 返回给您的每一块内存都需要额外的内存(是的,特殊例程可以减少这种情况......但您不知道!除非您自己实现它或购买一些奇迹,否则它的真正作用是-图书馆)。
【讨论】:
以上是关于更快地 malloc 多次小时间或几次大时间?的主要内容,如果未能解决你的问题,请参考以下文章