C 阵列性能与LinkedList非常相似-提供了什么?
所以这个标题有点误导。。。我将保持简单:我将比较这两种数据结构:C 阵列性能与LinkedList非常相似-提供了什么?,c,arrays,performance,unix,linked-list,C,Arrays,Performance,Unix,Linked List,所以这个标题有点误导。。。我将保持简单:我将比较这两种数据结构: 一个数组,它从大小1开始,对于随后的每次添加,都有一个realloc()调用来扩展内存,然后将新的(malloced)元素附加到n-1位置 一个链表,用来记录头部、尾部和大小。此外,还涉及新元素的mallocing和更新尾部指针和大小 不要担心这些数据结构的任何其他细节。这是我在测试中唯一关心的功能 理论上,LL的表现应该更好。然而,他们在涉及10,100,1000的时间测试中几乎相同。。。多达5000000个元素 我的直觉是这堆
(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
的工作原理,只需比较realloc
前后的指针即可。你应该发现它通常不会改变
我怀疑,由于您正在为每个元素调用realloc
(在生产中显然不明智),因此realloc
/malloc
调用本身是这两个测试的最大瓶颈,即使realloc
通常不提供新指针
此外,您还混淆了堆和数据段。堆是malloced内存所在的位置。数据段用于全局变量和静态变量。那么您正在测试数组相对于链表的扩展速度 在这两种情况下,您都在调用内存分配函数。通常,内存分配函数从操作系统中获取一块内存(可能是一页),然后根据应用程序的需要将其分成更小的部分
另一个假设是,realloc()有时会抛出虚拟内存并在其他地方分配一大块内存,因为它无法在当前分配的页面中获得连续的内存块。如果在列表展开之间没有对内存分配函数进行任何其他调用,则不会发生这种情况。也许操作系统对虚拟内存的使用意味着无论物理页来自何处,程序堆都在不断扩展。在这种情况下,性能将与一系列malloc()调用相同
当您混合使用malloc()和realloc()调用时,性能会发生变化。您是对的,realloc只会增加分配块的大小,除非阻止它这样做。在真实场景中,在列表的后续添加之间,很可能会在堆上分配其他对象?在这种情况下,realloc必须分配一个全新的内存块,并复制列表中已有的元素 尝试在堆上使用malloc为大约每十次插入分配另一个对象,看看它们是否仍然存在