realloc但只有前几个字节才有意义
假设我使用过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;
哪个更快?
它几乎肯定取决于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向下拆分缓冲区以创建一个新的可用内存块,但是当你再次重新分配备份时,分配器不会再将新的空闲块直接合并到缓冲区中,以便在不复制的情况下返回。
其中任何一个都可能使(2)明显比(1)贵得多。 所以这是一个实现细节,无论(2)是否是在(1)的优点(有时避免复制任何东西)和(3)的优点(有时避免复制太多)之间对冲你的赌注的好方法。
顺便说一句,这种关于性能的空闲猜测更有效,以便暂时解释你的观察,而不是暂时预测我们将在不太可能的事件中做出什么观察,我们实际上已经足够关注性能来测试它。
此外,我怀疑对于大型分配,通过将内存重新映射到新地址,实现可能甚至可以执行重定位realloc
而无需复制任何内容。 在这种情况下,他们都会很快。 不过,我还没有考虑实现是否真的这样做。
malloc
(对于整个块)和realloc
(对于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 | | | | | +-----------+ +-----------+
这可能取决于尺寸是多少以及是否需要复制。
方法1将复制旧块中包含的所有内容 – 但如果您不经常这样做,您将不会注意到。
方法2只会复制您需要保留的内容,因为您事先会丢弃其他所有内容。
方法3将无条件地复制,而其他方法仅在内存块无法resize的情况下进行复制。
就个人而言,如果你经常这样做,我更喜欢方法2,如果你做的更少,我更喜欢方法1。 我将分析哪些更快。