web-dev-qa-db-ja.com

なぜベクターを使用してデキューすることを好むのですか?

以来

  1. どちらも連続したメモリコンテナです。
  2. 機能的には、dequeにはベクターが持つほとんどすべての機能がありますが、前に挿入する方が効率的です。

誰もが好む理由std::vectorからstd::deque

75
Leon

dequeの要素は、メモリ内で連続しているnotです。 vector要素は保証されています。したがって、連続した配列を必要とするプレーンなCライブラリとやり取りする必要がある場合、または空間的な局所性に(かなり)関心がある場合は、vectorを好むかもしれません。さらに、追加のブックキーピングがあるため、他の操作はおそらく同等のvector操作よりも(わずかに)高価です。一方、vectorの多数/大規模なインスタンスを使用すると、不要なヒープフラグメンテーションが発生する可能性があります(newへの呼び出しが遅くなります)。

また、指摘したように StackOverflowの他の場所 には、より良い議論があります: http://www.gotw.ca/gotw/054.htm

103
phooji

違いを知るには、dequeの一般的な実装方法を知っておく必要があります。メモリは同じサイズのブロックで割り当てられ、それらは一緒に連鎖されます(配列または場合によってはベクトルとして)。

したがって、n番目の要素を見つけるには、適切なブロックを見つけて、その中の要素にアクセスします。常に2回のルックアップであるため、これは一定の時間ですが、それでもベクトルよりも多くなります。

vectorは、C APIであるか、ポインターと長さを取得できるという点で汎用性が高いため、連続したバッファーが必要なAPIでもうまく機能します。 (したがって、下にベクトルまたは通常の配列を作成し、メモリブロックからAPIを呼び出すことができます)。

dequeの最大の利点は次のとおりです。

  1. どちらかの端からコレクションを拡大または縮小する場合
  2. 非常に大きなコレクションサイズを扱う場合。
  3. Boolを扱う場合、ビットセットではなくboolが本当に必要です。

これらの2番目はあまり知られていませんが、非常に大きなコレクションサイズの場合:

  1. 再割り当てのコストが大きい
  2. 連続したメモリブロックを見つける必要があるため、オーバーヘッドが制限されるため、メモリをより速く使い果たすことができます。

過去に大きなコレクションを扱っていて、連続モデルからブロックモデルに移行したとき、32ビットシステムでメモリが不足する前に、約5倍のコレクションを保存できました。これは、再割り当ての際に、要素をコピーする前に古いブロックと新しいブロックを実際に保存する必要があるためです。

これをすべて言ったが、「楽観的な」メモリ割り当てを使用するシステムでは、std::dequeで問題が発生する可能性があります。 vectorの再割り当てのために大きなバッファサイズを要求する試みは、おそらくbad_allocである時点で拒否されますが、アロケータの楽観的な性質は、常にdequeによって要求されるより小さなバッファ。これにより、オペレーティングシステムがプロセスを強制終了して、メモリを獲得しようとする可能性があります。どれを選んでも、あまり快適ではないかもしれません。

そのような場合の回避策は、システムレベルのフラグを設定して楽観的な割り当てを無効にするか(常に実行可能であるとは限りません)、メモリを多少手動で管理することです。メモリ使用量などを確認する独自のアロケータを使用します。明らかに理想的ではありません。 (ベクトルを好むという質問に答えるかもしれません...)

31
CashCow

Vectorとdequeの両方を複数回実装しました。 dequeは、実装の観点から見ると非常に複雑です。この複雑さは、より多くのコードとより複雑なコードに変換されます。したがって、通常、ベクトルよりも両端キューを選択すると、コードサイズがヒットします。また、コードでベクターが優れているもの(Push_back)のみを使用している場合、わずかな速度低下が発生する可能性があります。

両端キューが必要な場合、dequeが明確な勝者です。ただし、ほとんどの挿入と消去をバックで行う場合は、ベクターが明確な勝者になります。確信が持てない場合は、typedefを使用してコンテナーを宣言し(そのため、簡単に切り替えられます)、測定します。

26
Howard Hinnant

std::dequeは、連続メモリを保証していません-インデックス付きアクセスの場合は、多少遅いことがよくあります。通常、両端キューは「ベクトルのリスト」として実装されます。

6
Erik

http://www.cplusplus.com/reference/stl/deque/ によると、「dequeはベクターとは異なり、すべての要素が連続したストレージ場所にあるとは限らないため、安全である可能性がなくなります。ポインタ演算によるアクセス。」

両端キューは、もう少し複雑です。これは、一部には必ずしも連続したメモリレイアウトがないためです。その機能が必要な場合は、両端キューを使用しないでください。

(以前、私の答えは標準化の欠如をもたらしました(上記と同じソースから、「異なる方法で特定のライブラリによって両端キューを実装することができます」)が、実際にはほぼすべての標準ライブラリデータ型に適用されます。)

2
patrickvacek

各ケースのパフォーマンステストを行うことをお勧めします。そして、このテストに基づいて決定を下します。

ほとんどの場合、std::dequeよりstd::vectorの方が好きです。

0
George Gaál

これらのテスト結果(ソースあり)に対応するdequeにベクターを好まないでしょう。

もちろん、アプリ/環境でテストする必要がありますが、要約すると:

  • Push_backは基本的にすべて同じです
  • 挿入、dequeでの消去は、リストよりもはるかに高速で、ベクトルよりもわずかに高速です

さらにいくつかの黙想、およびcircular_bufferを考慮するための注意事項

0
Nick Westgate

Dequeは、その要素へのランダムアクセスを許可するシーケンスコンテナですが、連続したストレージを持つことは保証されていません。

0
Sean

一方では、ベクターは非常に頻繁にdequeよりも単純に高速です。 dequeのすべての機能を実際に必要としない場合は、ベクトルを使用します。

一方で、時々doはベクターが提供しない機能を必要とします。その場合、dequeを使用する必要があります。たとえば、dequeを使用せず、アルゴリズムを大幅に変更せずに、 このコード の書き換えを試みるように、誰にでも挑戦します。

0
BenGoldberg