web-dev-qa-db-ja.com

FIFOに使用するSTLコンテナーはどれですか?

どのSTLコンテナが私のニーズに最適ですか?基本的に10要素幅のコンテナーがあり、その中で最も古い要素(約100万回)をPush_back処理しながら、pop_front新しい要素を継続的に格納しています。

現在、タスクにstd::dequeを使用していますが、自分自身を再割り当てする必要がないため、std::listの方が効率的かどうか疑問に思っていました(または、std::dequestd::vectorと間違えているかもしれません)。または、私のニーズにさらに効率的なコンテナがありますか?

追伸ランダムアクセスは必要ありません

82
Gab Royer

無数の答えがあるので、混乱するかもしれませんが、要約すると:

_std::queue_ を使用します。この理由は簡単です。FIFO構造です。FIFOが必要な場合は、_std::queue_を使用します。

それはあなたの意図を他の誰か、そしてあなた自身にさえ明確にします。 _std::list_ または _std::deque_ はありません。リストはどこでも挿入および削除できますが、これはFIFO構造体が想定していることではありません。また、dequeは両端で追加および削除できます。 FIFO構造体はできません。

これが、queueを使用する必要がある理由です。

さて、パフォーマンスについて尋ねました。まず、次の重要な経験則を常に覚えておいてください:最初に良いコード、最後にパフォーマンス

その理由は簡単です。清潔さとエレガンスがほとんど常に最後に終わる前にパフォーマンスを追求する人々。彼らのコードはドロドロになります。なぜなら、彼らはそれから何も得られないために良いことをすべて放棄したからです。

最初に適切で読みやすいコードを記述することにより、パフォーマンスの問題のほとんどが解決します。そして、後でパフォーマンスが不足していることに気付いた場合、プロファイラをニースのきれいなコードに追加して、問題の場所を見つけるのは簡単です。

つまり、_std::queue_は単なるアダプターです。安全なインターフェースを提供しますが、内部で別のコンテナーを使用します。この基礎となるコンテナを選択できます。これにより、かなりの柔軟性が得られます。

それで、どの基礎となるコンテナを使用すべきですか? _std::list_と_std::deque_の両方が必要な関数(Push_back()pop_front()、およびfront())を提供することを知っています。決めます?

まず、メモリの割り当て(および割り当て解除)は、OSにアクセスして何かを実行することを要求するため、一般に簡単なことではありません。 listは、何かが追加されるたびにメモリを割り当て、それがなくなるとメモリの割り当てを解除する必要があります。

一方、dequeはチャンクで割り当てます。 listよりも少ない頻度で割り当てます。リストと考えてください。ただし、各メモリチャンクは複数のノードを保持できます。 (もちろん、私はあなたに 実際にそれがどのように機能するかを学ぶ を提案するでしょう。

したがって、それだけではdequeのパフォーマンスが向上するはずです。これは、メモリを頻繁に処理しないためです。一定のサイズのデータ​​を処理しているという事実と混合すると、データを最初に通過した後に割り当てる必要はないでしょうが、リストは常に割り当てと割り当て解除を行います。

2番目に理解することは、 キャッシュパフォーマンス です。 RAMに行くのは遅いので、CPUが本当に必要なときは、メモリのチャンクをキャッシュに戻すことでこの時間を最大限に活用できます。dequeはメモリチャンクに割り当てます。このコンテナ内の要素にアクセスすると、CPUがコンテナの残りの部分を戻すことになる可能性があります。これで、dequeへの以降のアクセスは高速になります。キャッシュ内。

これは、データが一度に1つずつ割り当てられるリストとは異なります。これは、データがメモリ内のあらゆる場所に分散し、キャッシュのパフォーマンスが低下する可能性があることを意味します。

したがって、それを考慮すると、dequeがより適切な選択であるはずです。これが、queueを使用する場合のデフォルトのコンテナである理由です。確かに、これはまだ(非常に)経験に基づいた推測にすぎません。特定のテストでdequeを使用し、他のテストでlistを使用してこのコードをプロファイルする必要があります。

ただし、コードをクリーンなインターフェイスで動作させ、パフォーマンスを心配することを忘れないでください。

Johnは、listまたはdequeをラップするとパフォーマンスが低下するという懸念を提起します。繰り返しますが、彼も私も自分でプロファイルせずに確実に言うことはできませんが、コンパイラはqueueが行う呼び出しをインライン化する可能性があります。つまり、queue.Push()と言うと、実際にはqueue.container.Push_back()とだけ言って、関数呼び出しを完全にスキップします。

繰り返しになりますが、これは経験に基づいた推測に過ぎませんが、queueを使用しても、基礎となるコンテナrawを使用する場合と比較してパフォーマンスは低下しません。前に言ったように、queueを使用します。これは、クリーンで使いやすく、安全であり、実際に問題のプロファイルとテストになる場合があるためです。

173
GManNickG

チェックアウト std::queue。基礎となるコンテナタイプをラップし、デフォルトのコンテナはstd::deque

27
Mark Ransom

パフォーマンスが本当に重要な場合は、 ブースト循環バッファーライブラリ を確認してください。

10
Emile Cormier

私は絶えずPush_back新しい要素pop_front最も古い要素を使用します(約100万回)。

コンピューティングでは、100万人は実際にはそれほど多くありません。他の人が示唆しているように、std::queue最初の解決策として。あまりにも遅い場合は、プロファイラーを使用してボトルネックを特定し(推測しないでください!)、同じインターフェースを持つ別のコンテナーを使用して再実装します。

7
anon

何故なの std::queue?それはすべてPush_backおよびpop_front

5
eduffy

queue はおそらく deque よりもシンプルなインターフェイスですが、このような小さなリストの場合、パフォーマンスの違いはおそらく無視できる程度です。

list についても同様です。必要なAPIを選択するだけです。

3
lavinio