使用new,malloc等动态内存分配的时间复杂度是多少?我对内存分配器的实现方式知之甚少,但我认为答案是它取决于实现。因此,请回答一些更常见的案例/实施。
编辑: 我依稀记得在最坏的情况下听说堆分配是无限的,但我真的对平均/典型情况感兴趣。
答案 0 :(得分:28)
在处理O表示法时,您必须意识到的一件事是,理解 n 是什么通常非常重要。如果 n 与您可以控制的内容相关(例如:您想要排序的列表中的元素数量),那么仔细观察它是有意义的。
在大多数堆实现中, n 是管理器正在处理的连续内存块的数量。这绝对是不通常由客户控制的东西。客户端真正控制的唯一 n 是她想要的内存块的大小。通常这与分配器所花费的时间无关。大型 n 可以像小型 n 一样快速分配,或者可能需要更长时间,或者甚至可能无法使用。所有这些都可以针对相同的 n 进行更改,具体取决于之前的其他客户端的分配和解除分配方式。实际上,除非您正在实现堆,否则正确的答案是它是非-deterministic 强>
这就是硬实时程序员试图避免动态分配(启动后)的原因。
答案 1 :(得分:22)
堆分配器的时间复杂度在不同系统上可能会有所不同,具体取决于它们可能优化的内容。
在桌面系统上,堆分配器可能使用不同策略的混合,包括缓存最近的分配,常见分配大小的后备列表,具有特定大小特征的内存块的容器等,以尝试保持分配时间,但也保持碎片可管理。有关使用的各种技术的概述,请参阅Doug Lea的malloc实现说明:http://g.oswego.edu/dl/html/malloc.html
对于更简单的系统,可以使用第一拟合或最佳拟合的策略,其中空闲块存储在链表上(其将给出O(N)时间,其中N是空闲块的数量)。但是可以使用更复杂的存储系统(如AVL树)来在O(log N)时间(http://www.oocities.org/wkaras/heapmm/heapmm.html)中找到空闲块。
实时系统可能使用堆分配器,如TLSF(两级隔离匹配),其分配成本为O(1):http://www.gii.upv.es/tlsf/
答案 2 :(得分:2)
我认为它通常是O(n),其中n是可用内存块的数量(因为你必须扫描可用的内存块以找到合适的内存块)。
话虽如此,我已经看到可以使其更快的优化,特别是根据它们的大小范围维护多个可用块列表(因此一个列表中小于1k的块,另一个列表中的块从1k到10k)等等。)
然而,这仍然是O(n),只是使用较小的n。
我有兴趣看到你的消息来源有一个无限的堆分配(如果,那么,你的意思是它可能需要永远)。
答案 3 :(得分:2)
查看典型分配器的工作原理。
指针碰撞分配器在 O(1)中工作,并且它是一个小的“ 1 ”。
对于隔离存储分配器,k字节的分配意味着“从List返回第一个块( n )”,其中List( n )是块列表n个字节,其中 n> = k 。它可能发现List( n )为空,因此下一个列表中的块(List( 2n ))必须是将所有 n 字节的块放到List( n )上进行拆分,此效果可能会波动所有可用的大小,从而形成一个 O(ns)的复杂性,其中ns是可用的不同大小的数量, ns = log(N)其中 N 的大小为最大的块大小可用,所以即使这样也会很小。在大多数情况下,特别是在分配和释放了大量块后,复杂性为 O(1)。
答案 4 :(得分:2)
只有两个评论:
TLSF是O(1),意思是没有单个循环;并管理高达2Gb。 虽然很难相信,但只需检查代码。
“最适合”的政策(找到紧密的区块)是最合适的 实现小碎片化。 证明这一说法远非微不足道,事实上它尚未得到正式证实,但有许多证据朝这个方向发展。 (很好的研究课题)。