【发布时间】:2011-04-19 08:05:10
【问题描述】:
所以标题有点误导......我会保持简单:我正在比较这两种数据结构:
- 一个数组,它从大小 1 开始,对于每个后续添加,都会调用 realloc() 来扩展内存,然后将新的(分配的)元素附加到 n-1 位置。
- 一个链表,用来跟踪头部、尾部和大小。并且添加涉及分配新元素并更新尾指针和大小。
不要担心这些数据结构的任何其他细节。这是我在此测试中唯一关心的功能。
理论上,LL 应该表现更好。但是,它们在涉及 10、100、1000... 最多 5,000,000 个元素的时间测试中几乎相同。
我的直觉是堆很大。我认为 Redhat 上的数据段默认为 10 MB?我可能是错的。无论如何,realloc() 首先检查在已分配的连续内存位置 (0-[n-1]) 的末尾是否有可用空间。如果第 n 个位置可用,则不存在元素的重定位。相反, realloc() 只保留旧空间 + 紧随其后的空间。我很难找到这方面的证据,而且我也很难证明这个阵列在实践中的性能应该比 LL 差。
在阅读以下帖子后,这里有一些进一步的分析:
[更新 #1] 我已经修改了代码,使其具有一个单独的列表,该列表在 LL 和 Array 的每 50 次迭代中分配一次内存。对于阵列中的 100 万个添加,几乎始终有 18 个移动。 LL没有搬家的概念。我做了一个时间比较,它们仍然几乎相同。以下是增加 1000 万次的一些输出:
(Array)
time ./a.out a 10,000,000
real 0m31.266s
user 0m4.482s
sys 0m1.493s
(LL)
time ./a.out l 10,000,000
real 0m31.057s
user 0m4.696s
sys 0m1.297s
我预计 18 步的时间会大不相同。数组加法需要 1 次赋值和 1 次比较来获取并检查 realloc 的返回值,以确保发生移动。
[更新 #2] 我在上面发布的测试中运行了 ltrace,我认为这是一个有趣的结果......看起来 realloc(或某些内存管理器)正在根据当前大小抢先将数组移动到更大的连续位置。 对于 500 次迭代,在迭代中触发了内存移动: 1、2、4、7、11、18、28、43、66、101、154、235、358 这非常接近求和序列。我觉得这很有趣 - 我想我会发布它。
【问题讨论】:
-
你可以很容易地找到
realloc()扩展块而不是移动它的证据 - 如果指针realloc()返回的指针与你传递它的指针相同,那么它扩展了块。跨度> -
如果您想知道
realloc是否真的需要复制数据,只需输入一些代码来比较调用前后的指针。它们不同的时间是它复制的时间。每次复制时将数组大小加起来,您会看到数组有多少开销。 -
有些系统每次需要重新分配时都会将内存分配乘以 2(或大约);这大大减少了操作时间。
标签: c arrays performance unix linked-list