C - 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;

哪个更快?

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_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_sizeold_size小得多,并且new_size处于realloc合理地可能重新定位的范围内,但也合理地不太可能。那么你无法预测(1)和(3)中的哪一个会比(2)略快。

在分析 (2) 中,我假设向下的 realloc 大约是自由的,并且返回相同的指针。这不能保证。我能想到两件事会让你感到困惑:

  • Realloc 向下复制到新的分配
  • Realloc
  • 向下拆分缓冲区以创建新的可用内存块,但是当您再次备份 Realloc 时,分配器不会将该新的可用块直接合并回缓冲区,以便返回而不复制。

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

顺便说一句,这种关于性能的闲置猜测为了暂时解释你的观察更有效,而不是试探性地预测我们会做出什么观察,以防万一我们实际上足够关心性能来测试它。

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

这可能取决于大小以及是否需要复制。

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

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

方法3 将无条件复制,而其他方法仅在内存块无法调整其大小时才复制。

就个人而言,如果您经常这样做,我更喜欢方法 2,如果您很少这样做,我更喜欢方法 1。我将分别分析其中哪一个会更快。

相关内容

  • 没有找到相关文章

最新更新