8

私がバイト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;

どちらが速いですか?

4

3 に答える 3

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です。これは、内部で最適化される可能性が高いためです。

収縮と拡張のために2回呼び出すか、呼び出すことmalloc-new/memcpy/free-oldが効率的である可能性はほとんどありませんが、すべての最適化と同様に、測定する必要があります。推測しないでください。

realloc必ずしもメモリをコピーする必要はないことに注意してください。拡張を適切に実行できる場合、インテリジェントヒープマネージャーは、次のようなものをコピーせずに、ブロックのサイズを増やすだけです。

+-----------+   ^        +-----------+ <- At same address,
| Old block |   | Need   | New block |      no copying
|           |   | this   |           |      involved.
+-----------+   | much   |           |
| Free      |   | now.   |           |
|           |   v        +-----------+
|           |            | Free      |
|           |            |           |
+-----------+            +-----------+
于 2012-11-06T09:00:11.247 に答える
2

ほぼ確実に、、、の値にold_size依存new_sizeheader_size、実装にも依存します。いくつかの値を選択して測定する必要があります。

header_size == old_size-1 && old_size == new_size-11)は、シングルreallocが基本的にノーオペレーションである可能性が最も高いため、おそらく最適です。(2)その場合、非常にわずかに遅くなるはずです(2つのほとんど操作がない場合は1よりわずかに遅くなります)。

header_size == 1 && old_size == 1024*1024 && new_size == 2048*10243)は、割り当てを移動する必要があるため、おそらく最適ですがrealloc、気にしない1MBのデータをコピーすることは避けてください。(2)その場合、非常にわずかに遅くなるはずです。

header_size2)がよりもはるかに小さく、が移動する可能性がかなり高い範囲内にある場合old_sizeに、おそらく最適ですが、移動しない可能性かなりあります。その場合、(1)と(3)のどちらが、(2)よりもわずかに高速になるかを予測することはできません。new_sizerealloc

(2)の分析では、下向きの再割り当てはほぼ自由であり、同じポインターを返すと仮定しました。これは保証されません。私はあなたを台無しにすることができる2つのことを考えることができます:

  • 新しい割り当てにコピーを下向きに再割り当てします
  • reallocはバッファを下向きに分割して空きメモリの新しいチャンクを作成しますが、reallocを再度バックアップしても、コピーせずに戻るために、アロケータはその新しい空きチャンクをバッファに直接マージしません。

これらのいずれかにより、(2)は(1)よりも大幅に高価になる可能性があります。したがって、(2)が(1)の利点(コピーを回避する場合もある)と(3)の利点(コピーを過剰に回避する場合)の間で賭けをヘッジする良い方法であるかどうかは、実装の詳細です。

ところで、パフォーマンスに関するこの種の怠惰な推測は、パフォーマンスをテストするのに十分な注意を払ったという万が一の場合にどのような観察を行うかを暫定的に予測するよりも、観察を暫定的に説明するために効果的です。

reallocさらに、大規模な割り当ての場合、実装は、メモリを新しいアドレスに再マップすることにより、何もコピーせずに再配置することさえできるかもしれないと思います。その場合、それらはすべて高速になります。ただし、実装が実際にそれを行うかどうかについては調べていません。

于 2012-11-06T09:33:08.340 に答える
1

それはおそらくサイズが何であるか、そしてコピーが必要かどうかに依存します。

方法1は、古いブロックに含まれているすべてのものをコピーしますが、あまり頻繁に行わないと、気付かないでしょう。

方法2は、他のすべてを事前に破棄するため、保持する必要があるものだけをコピーします。

方法3は無条件にコピーしますが、他の方法は、メモリブロックのサイズを変更できない場合にのみコピーします。

個人的には、これを頻繁に行う場合は方法2を、めったに行わない場合は方法1をお勧めします。それぞれ、私はこれらのどれがより速いかをプロファイリングします。

于 2012-11-06T09:04:43.960 に答える