web-dev-qa-db-ja.com

容量を `std :: vector`のサイズまで削減する適切な方法は、shrink_to_fitですか?

C++ 11では、shrink_to_fitは、特定のSTLコンテナーを補完するために導入されました(例:std::vectorstd::dequestd::string)。

要約すると、その主な機能は、関連付けられているコンテナを要求することです容量を縮小してサイズに合わせる。ただし、このリクエストは非バインディングであり、コンテナの実装はそれ以外の場合は自由に最適化し、そのサイズより大きい容量のベクトルを残すことができます。

さらに、以前のSO質問では、OPはshrink_to_fitを使用してstd::vectorの容量をそのサイズまで削減することは推奨されていませんでした。そうしない理由は引用されています未満:

shrink_to_fitは何もしないか、キャッシュの局所性の問題を引き起こし、O(n)を実行します(各アイテムを新しいアイテムにコピーする必要があるため、小さな家。通常、余裕をメモリに残す方が安くなります @ Massa

誰かが次の質問に答えるほど親切でしたか?

  • 引用の議論は成り立つか?
  • 「はい」の場合、STLコンテナの容量をそのサイズに縮小する適切な方法は何ですか(少なくともstd::vectorの場合)。
  • そして、コンテナを縮小するより良い方法がある場合、結局shrink_to_fitが存在する理由は何ですか?
26
101010

引用の議論は成り立つか?

測定し、あなたは知っています。あなたは記憶に制約がありますか?正しいサイズを事前に把握できますか?事後のshrinkよりもreserveの方が効率的です。一般的に、ほとんどの用途はたぶんスラックで問題ないという前提に同意する傾向があります。

はいの場合、STLコンテナの容量をそのサイズに縮小する適切な方法は何ですか(少なくともstd :: vectorの場合)。

コメントは_shrink_to_fit_だけでなく、その他の縮小方法にも適用されます。 reallocを設定できない場合、縮小に使用するメカニズムに関係なく、別のメモリチャンクを取得してそこにコピーする必要があります。

そして、もしコンテナを縮小するより良い方法があるとしたら、やっぱりshrink_to_fitが存在する理由は何ですか?

リクエストは拘束力はありませんが、代替案はより良い保証がありません。問題は、shrinkingが理にかなっているかどうかです。もしそうなら、オブジェクトが存在するという事実を利用できる_shrink_to_fit_演算を提供することは理にかなっています新しい場所に移動。つまり、タイプTnoexcept(true)移動コンストラクターがある場合、新しいメモリを割り当てて要素を移動します。

外部でも同じことを実現できますが、このインターフェイスにより操作が簡単になります。 C++ 03の_shrink_to_fit_に相当するものは次のとおりです。

_std::vector<T>(current).swap(current);
_

しかし、このアプローチの問題は、一時ファイルへのコピーが行われたときに、currentが置き換えられることを認識していないことであり、ライブラリーに保持されているオブジェクトを移動します。 std::move(current)を使用しても、同じcapacity()を維持しながら、バッファー全体を移動する場合のように、望ましい効果が得られないことに注意してください。

これを外部で実装することは少し面倒です:

_{
   std::vector<T> copy;
   if (noexcept(T(std::move(declval<T>())))) {
      copy.assign(std::make_move_iterator(current.begin()),
                  std::make_move_iterator(current.end()));
   } else {
      copy.assign(current.begin(), current.end());
   }
   copy.swap(current);
}
_

私がif条件を正しく取得したと仮定すると...これは、この操作が必要になるたびに記述したいとは思わないでしょう。

  • 議論は成立しますか?

議論は元々私のものなので、私がそれらを一つずつ守っても構わない:

  1. _shrink_to_fit_は何もしません(...)

    それが言及されたように、標準は(何度もですが、vectorの場合はセクション23.3.7.3です...)リクエストは、最適化の実装の自由度を可能にするために非拘束的ですです。これは、実装canが_shrink_to_fit_を何もしないものとして定義することを意味します。

  2. (...)またはそれはあなたにキャッシュ局所性問題を与えます

    _shrink_to_fit_がnotがno-opとして実装されている場合、容量size()、copy(または、最高のケース、移動)古いアイテムからすべてのN = size()新しいアイテムを作成し、古いアイテムをすべて破棄します(移動の場合はこれを最適化する必要がありますが、これには古いコンテナーに対するループが再び含まれる可能性があります)。その後、古いコンテナ自体を破壊します。これは、David Rodriguezが説明したとおり、_libstdc++-4.9_で行われます。

    _      _Tp(__make_move_if_noexcept_iterator(__c.begin()),
              __make_move_if_noexcept_iterator(__c.end()),
              __c.get_allocator()).swap(__c);
    _

    および_libc++-3.5_では、ほぼ同じ機能を果たす___alloc_traits_の関数によって。

    ああ、そして実装絶対に不可能reallocに依存します(メモリ割り当てに_::operator new_内でmallocを使用している場合でも)reallocは、インプレースで縮小できない場合、メモリを残します単独(操作なしの場合)またはビット単位のコピーを作成します(適切なC++コピー/移動コンストラクターが与えるポインターなどを再調整する機会を逃します)。

    もちろん、縮小可能なメモリアロケータを作成して、そのベクトルのコンストラクタで使用できます。

    ベクトルがキャッシュラインよりも大きい簡単なケースでは、そのすべての動きがキャッシュに圧力をかけます。

  3. そしてそれはO(n)です

    n = size()の場合、少なくとも、nサイズの割り当て、n構造のコピーまたは移動、nの破棄、および_old_capacity_サイズの割り当て解除の1つを実行する必要があることを上に確立されたと思います。

  4. 通常、余裕をメモリに残すだけの方が安い

    明らかに、実際に空きメモリを求められない限り(この場合、データをディスクに保存し、後でオンデマンドで再ロードする方が賢明かもしれません...)

  • はいの場合、STLコンテナの容量をそのサイズに縮小する適切な方法は何ですか(少なくともstd :: vectorの場合)。

適切な方法はまだ_shrink_to_fit_...に依存しないか、実装をよく知っている必要があります!

  • そして、もしコンテナを縮小するより良い方法があるとしたら、やっぱりshrink_to_fitが存在する理由は何ですか?

より良い方法はありませんが、_shrink_to_fit_が存在する理由は、AFAICTです。これは、プログラムがメモリのプレッシャーを感じることがあり、それを処理する1つの方法である場合があるためです。あまり良い方法ではありませんが、それでもなおです。

HTH!

14
Massa
  • はいの場合、STLコンテナの容量をそのサイズに縮小する適切な方法は何ですか(少なくともstd :: vectorの場合)。

「スワップトリック」は、ベクターを必要な正確なサイズにトリミングします(より効果的なSQLから)。

vector<Person>(persons).swap(persons);

すべてのメモリを解放するために、ベクターが空の場合に特に役立ちます。

vector<Person>().swap(persons);

未使用スペースの割り当てが保持されているため、ベクターがユニットテスターのメモリリーク検出コードを常にトリップし、これによりそれらが完全に整理されました。

これは、実行時の効率(サイズまたは速度)を実際には気にしない種類の例ですが、正確なメモリ使用量を気にします。

  • そして、もしコンテナを縮小するより良い方法があるならば、結局はshrink_to_fitが存在する理由は何ですか?

合法的に全く何もできない機能を提供する意味が何なのか本当にわかりません。それが導入されたのを見たとき私は歓声を上げ、それが信頼できないとわかったとき絶望した。

たぶん、次のバージョンでmaybe_sort()が見つかるでしょう。

1
Andy Krouwel