17

std::reallocmallocされたメモリにポッド以外のタイプが含まれている場合、C++では危険です。唯一の問題は、std::reallocメモリをその場で拡張できない場合に型デストラクタを呼び出さないことです。

ささいな回避策はtry_realloc関数です。in situで拡張できない場合、新しいメモリをmallocする代わりに、単にfalseを返します。この場合、新しいメモリを割り当てることができ、オブジェクトは新しいメモリにコピー(または移動)され、最後に古いメモリが解放されます。

これは非常に便利なようです。 std::vectorこれを大いに活用して、すべてのコピー/再割り当てを回避できる可能性があります。
プリエンプティブ難燃剤:技術的には、これはBig-Oのパフォーマンスと同じですが、ベクトルの成長がアプリケーションのボトルネックである場合、Big-Oが変更されていない場合でも、2倍のスピードアップが最適です。

しかし、のように機能するcapiが見つかりませんtry_realloc

私は何かが足りないのですか?私try_reallocが想像するほど役に立たないのですか?使用できなくなる隠れたバグはありtry_reallocますか?

さらに良いことに、次のように機能する、文書化されていないAPIはありますtry_reallocか?

注:私は明らかに、ここのライブラリ/プラットフォーム固有のコードにいます。try_realloc本質的に最適化であるため、私は心配していません。


更新:vector reallocを使用する方が効率的 かどうかについてのSteve Jessopsのコメントに続いて、テストする概念実証を作成しました。はrealloc-vectorベクトルの成長パターンをシミュレートしますが、代わりに再割り当てするオプションがあります。ベクトル内の最大100万要素までプログラムを実行しました。

比較のためvectorに、100万要素に成長しながら19回割り当てる必要があります。

結果は、ヒープを使用する唯一のものである場合、realloc-vector結果は素晴らしいものであり、100万バイトのサイズに成長しながら3〜4の割り当てが行われます。

を66%の速度で成長するrealloc-vectoraと一緒に使用すると、結果 はあまり期待できず、成長中に8〜10回割り当てられます。vectorrealloc-vector

最後に、が同じ速度で成長するrealloc-vectoraと一緒に使用される場合、は17〜18回割り当てられます。標準のベクトル動作よりも1つの割り当てをほとんど節約できません。vectorrealloc-vector

ハッカーが節約を改善するために割り当てサイズをゲームできることは間違いありませんが、そのようなアロケータを作成して維持するための多大な努力は利益をもたらさないというスティーブに同意します。

4

3 に答える 3

11

vector通常、大きく増加します。ベクターの内部バッファーのすぐ上に大量の空きアドレスが存在するように注意深く配置しない限り、再配置せずにこれを繰り返し行うことはできません(明らかに他の割り当てを行うことができないため、実際にはページ全体を割り当てる必要があります)後で同じページで)。

したがって、ここで本当に優れた最適化を実現するには、可能であれば安価な再割り当てを行う「簡単な回避策」以上のものが必要だと思います。それを可能にするために何らかの準備を行う必要があり、その準備にはスペースに対処するためのコストがかかります。 。大きくなることを示す特定のベクトルに対してのみそれを行う場合、それらが大きくなることを示すことができるため、それはかなり無意味reserve()です。広大なアドレス空間がある場合にのみ、すべてのベクトルに対して自動的に実行できるため、すべてのベクトルでその大きなチャンクを「無駄にする」ことができます。

私が理解しているように、このAllocator概念に再割り当て機能がない理由は、単純にするためです。std::allocator関数がある場合は、try_reallocすべてのアロケータに関数が必要です(ほとんどの場合、実装できず、常にfalseを返す必要があります)。そうでない場合は、すべての標準コンテナをstd::allocator利用するために特化する必要があります。それ。どちらのオプションも優れたAllocatorインターフェースではありませんが、ほとんどすべてのAllocatorクラスの実装者にとって、何もしないtry_realloc関数を追加するだけではそれほど大きな労力はないと思います。

vector再割り当てが原因で遅い場合はdeque、適切な代替品となる可能性があります。

于 2010-11-03T16:38:32.237 に答える
4

withとwithを使用して、try_realloc提案したようなものを実装できます。mmapMAP_ANONYMOUSMAP_FIXEDmremapMREMAP_FIXED

編集:mremapのmanページにも次のように書かれていることに気づきました:

mremap()は、Linuxページテーブルスキームを使用します。mremap()は、仮想アドレスとメモリページ間のマッピングを変更します。これは、非常に効率的なrealloc(3)を実装するために使用できます。

于 2010-11-03T16:29:53.670 に答える
2

reallocCでは、便利な関数以上のものはほとんどありません。パフォーマンス/コピーの削減にはほとんどメリットがありません。私が考えることができる主な例外は、大きな配列を割り当て、必要なサイズがわかったらサイズを減らすコードです-しかし、これでも一部のmalloc実装(ブロックをサイズで厳密に分離する実装)でデータを移動する必要がある場合があるため、この使用法を検討しますrealloc本当に悪い習慣。

要素を追加するたびに配列を常に再割り当てするのではなく、スペースが不足するたびに配列を指数関数的に(たとえば、25%、50%、または100%)大きくし、新しいメモリを手動で割り当てるだけである限り、古いものをコピーして解放すると、を使用した場合とほぼ同じ(メモリの断片化の場合は同じ)パフォーマンスが得られreallocます。これは確かにC++STL実装が使用するアプローチであるため、あなたの懸念全体は根拠がないと思います。

編集realloc:実際に役立つ1つの(まれですが前代未聞ではない)ケースは、仮想メモリを備えたシステム上の巨大なブロックで、Cライブラリがカーネルと対話してページ全体を新しいアドレスに再配置します。これがまれであると私が言う理由は、ほとんどの実装がページ粒度の割り当てを処理する領域に入る前に、非常に大きなブロック(少なくとも数百kB)を処理する必要があり、おそらくはるかに大きい(おそらく数MB)ためです。カーネルスペースに出入りして仮想メモリを再配置する前は、単にコピーを実行するよりも安価です。もちろんtry_realloc、ここでは役に立ちません。全体のメリットは、実際に安価に移動することから得られるからです。

于 2010-11-03T18:39:19.043 に答える