>假设我使用ptr = malloc(old_size);
来分配一个old_size
字节的内存块。只有前header_size
个字节有意义。我要将大小增加到new_size
.
new_size
大于old_size
,old_size
大于header_size
。
以前:
/- - - - - - - old_size - - - - - - -
+===============+---------------------+
-header_size-/
后:
/- - - - - - - - - - - - - - - new_size - - - - - - - - - - - - - - - - - - -
+===============+------------------------------------------------------------+
- header_size-/
我不在乎ptr + header_size
后存储什么,因为我会在那里读取一些数据。
方法1:直接进入new_size
ptr = realloc(ptr, new_size);
方法2:收缩到header_size
,增长到new_size
ptr = realloc(ptr, header_size);
ptr = realloc(ptr, new_size);
方法 3:分配新的内存块并复制前header_size
个字节
void *newptr = malloc(new_size);
memcpy(newptr, ptr, header_size);
free(ptr);
ptr = newptr;
哪个更快?
malloc
(对于整个块)和realloc
(对于增加大小时超出旧块大小的空间)都不能保证您收到的内存将包含什么,因此,如果您希望将这些多余的字节设置为零(例如),则必须使用以下方法自己执行此操作:
// ptr contains current block.
void *saveptr = ptr;
ptr = realloc (ptr, new_size);
if (ptr == NULL) {
// do something intelligent like recover saveptr and exit.
}
memset (ptr + header_size, 0, new_size - header_size);
但是,由于您已经声明您不关心标题以外的内容,因此几乎可以肯定最快的是单个realloc
因为它可能会在幕后进行优化。
调用它两次进行收缩和扩展,或者调用malloc-new/memcpy/free-old
不太可能像所有优化一样有效,您应该衡量,不要猜测!
请记住,realloc
不一定必须复制您的内存。如果扩展可以就地完成,那么智能堆管理器将只增加块的大小而不复制任何内容,例如:
+-----------+ ^ +-----------+ <- At same address,
| Old block | | Need | New block | no copying
| | | this | | involved.
+-----------+ | much | |
| Free | | now. | |
| | v +-----------+
| | | Free |
| | | |
+-----------+ +-----------+
它几乎肯定取决于old_size
、new_size
和header_size
的值,也取决于实现。您必须选择一些值并进行测量。
1)在header_size == old_size-1 && old_size == new_size-1
的情况下可能是最好的,因为它给了你单realloc
基本上是无操作的最佳机会。(2) 在这种情况下应该只是稍微慢一点(2 个几乎无操作比 1 略慢)。
3)可能是最好的情况下header_size == 1 && old_size == 1024*1024 && new_size == 2048*1024
,因为realloc
必须移动分配,但您可以避免复制1MB您不关心的数据。(2)在这种情况下应该只是稍微慢一点。
2)可能是最好的,当header_size
比old_size
小得多,并且new_size
处于realloc
合理地可能重新定位的范围内,但也合理地不太可能。那么你无法预测(1)和(3)中的哪一个会比(2)略快。
在分析 (2) 中,我假设向下的 realloc 大约是自由的,并且返回相同的指针。这不能保证。我能想到两件事会让你感到困惑:
- Realloc 向下复制到新的分配 Realloc
- 向下拆分缓冲区以创建新的可用内存块,但是当您再次备份 Realloc 时,分配器不会将该新的可用块直接合并回缓冲区,以便返回而不复制。
其中任何一个都可能使(2)比(1)贵得多。因此,(2)是否是(1)(有时避免复制任何东西)和(3)(有时避免复制太多)的优点之间的对冲赌注的好方法,这是一个实现细节。
顺便说一句,这种关于性能的闲置猜测为了暂时解释你的观察更有效,而不是试探性地预测我们会做出什么观察,以防万一我们实际上足够关心性能来测试它。
此外,我怀疑对于大量分配,通过将内存重新映射到新地址,实现甚至可以在不复制任何内容的情况下执行重新定位realloc
。在这种情况下,它们都会很快。不过,我还没有研究实现是否真的这样做。
这可能取决于大小以及是否需要复制。
方法 1 将复制旧块中包含的所有内容 - 但如果您不经常这样做,您就不会注意到。
方法 2 只会复制您需要保留的内容,因为您事先会丢弃其他所有内容。
方法3 将无条件复制,而其他方法仅在内存块无法调整其大小时才复制。
就个人而言,如果您经常这样做,我更喜欢方法 2,如果您很少这样做,我更喜欢方法 1。我将分别分析其中哪一个会更快。