realloc但只有前几个字节才有意义

假设我使用过ptr = malloc(old_size); 使用old_size字节分配内存块。 只有第一个header_size字节才有意义。 我要将大小增加到new_size

new_size大于old_sizeold_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_sizenew_sizeheader_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_sizerealloc重新定位的合理可能范围内,但很可能不会。 那么你无法预测(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。 我将分析哪些更快。