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

时间:2012-11-06 08:56:08

标签: c memory-management malloc realloc libc

假设我使用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;

哪个更快?

3 个答案:

答案 0 :(得分:3)

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      |
|           |            |           |
+-----------+            +-----------+

答案 1 :(得分:2)

它几乎肯定取决于old_sizenew_sizeheader_size的值,而且还取决于实现。你必须选择一些值并进行衡量。

1)在header_size == old_size-1 && old_size == new_size-1的情况下可能是最好的,因为它为您提供了单realloc基本上是无操作的最佳机会。 (2)在这种情况下应该只是稍微慢一点(2几乎没有操作比1略慢)。

header_size == 1 && old_size == 1024*1024 && new_size == 2048*1024的情况下,

3)可能是最好的,因为realloc必须移动分配,但是你要避免复制你不关心的1MB数据。 (2)在这种情况下应该只是稍微慢一些。

header_sizeold_size小得多时,

2)可能是最佳的,new_size处于realloc重新定位的合理可能范围内,但< em>也它很可能不会。那么你无法预测(1)和(3)中的哪一个会比(2)稍快一些。

在分析(2)时,我假设向下重新分配大约是空闲的并返回相同的指针。这不保证。我可以想到两件可能让你搞砸的事情:

  • 向下重新分配副本到新分配
  • realloc向下拆分缓冲区以创建一个新的可用内存块,但是当你再次重新分配备份时,分配器不会将新的空闲块直接再次合并到缓冲区中没有复制就返回。

其中任何一个都可能使(2)比(1)贵得多。所以这是一个实现细节,无论是否(2)是在(1)的优点(有时避免复制任何东西)和(3)的优点(有时避免复制太多)之间对冲你的赌注的好方法。

顺便说一下,这种关于性能的闲置猜测更有效,以便暂时解释你的观察结果,而不是试探性地预测我们会在不太可能发生的情况下做出什么样的观察,我们实际上已经足够关注性能来测试它。 / p>

此外,我怀疑对于大型分配,通过将内存重新映射到新地址,实现可能甚至可以执行重定位realloc而无需复制任何内容。在这种情况下,他们都会很快。不过,我没有考虑实现是否真的这样做。

答案 2 :(得分:1)

这可能取决于尺寸是多少以及是否需要复制。

方法1将复制旧块中包含的所有内容 - 但如果您不经常这样做,则不会注意到。

方法2只会复制您需要保留的内容,因为您事先会丢弃其他所有内容。

方法3将无条件地复制,而其他方法仅在内存块无法调整大小的情况下进行复制。

就个人而言,如果你经常这样做,我会更喜欢方法2,如果你做的更少,我更喜欢方法1。我将分析哪些内容会更快。