web-dev-qa-db-ja.com

stlマップのパフォーマンス?

使っています map<MyStruct, I*> map1;。どうやら私のアプリの合計時間の9%はそこで費やされています。具体的には、私の主要な機能の1行にあります。マップはそれほど大きくありません(ほとんど常に1k未満、20未満が一般的です)。

使用したい代替実装はありますか?私は自分で書くべきではないと思いますが、それが良い考えだと思ったら書けます。

追加情報:要素を追加する前に必ず確認します。キーが存在する場合、問題を報告する必要があります。ポイントの後よりも、ルックアップにマップを多用し、要素を追加しません。

20
user34537

まず、マップとは何か、実行している操作は何を表しているのかを理解する必要があります。 _std::map_はバランスの取れたバイナリツリーです。ルックアップにはO( log N )演算が必要です。それぞれの演算は、キーといくつかの追加の比較であり、ほとんどの場合は無視できます(ポインタ管理)。挿入は、挿入ポイントの特定と、新しいノードの割り当て、ツリーへの実際の挿入と再調整にほぼ同じ時間かかります。複雑さはO( log N )ですが、隠れた定数はより高くなっています。

挿入前にキーがマップ内にあるかどうかを判断しようとすると、ルックアップのコストが発生し、それが成功しない場合は、挿入ポイントを見つけるための同じコストがかかります。 _std::map::insert_を使用してイテレータとブール値のペアを返し、挿入が実際に発生したか、要素がすでに存在しているかを通知することにより、追加のコストを回避できます。

それを超えて、あなたはあなたのキーを比較することがどれほどコストがかかるかを理解する必要があります、それは質問が示すものから外れます(MyStructは1つだけintまたはそれらの千を保持することができます)、それはですあなたが考慮に入れる必要がある何か。

最後に、mapがニーズに対して最も効率的なデータ構造ではない場合があり、一定の時間の挿入が予想される_std::unordered_map_(ハッシュテーブル)の使用を検討できます。 (ハッシュ関数がhorribleでない場合)または小さなデータセットの場合、バイナリ検索を使用して要素を見つけることができる単純な配列(または_std::vector_)でも(これにより、割り当ての数。ただし、挿入のコストは高くなりますが、保持される型が十分に小さい場合は、それに値する可能性があります)

いつものように、パフォーマンスを測定し、時間を費やしている場所を理解してください。また、アプリケーションが何であるかによって、特定の関数またはデータ構造に費やされる時間の10%は、ほとんどまたはまったくない場合があります。たとえば、アプリケーションがデータセットへの検索と挿入を実行しているだけで、CPUの10%しか使用しない場合、他のあらゆる場所で最適化するために多くのことをします!

おそらくinsertを実行してpair.secondfalseです。キーがすでに存在する場合:

このような

if ( myMap.insert( make_pair( MyStruct, I* ) ).second == false)
{
  // report error
}
else
  // inserted new value

...毎回findを呼び出すのではなく、.

9
EdChum

mapの代わりに、ツリーの代わりにハッシュキーを使用して要素を見つける unordered_map を試すことができます。 この答え は、mapよりもunordered_mapを優先する場合のヒントを示します。

7
Christian Ammer

長いショットかもしれませんが、小さなコレクションの場合、最も重要な要素は cache パフォーマンスです。

std::mapは赤黒木を実装しているため、[AFAIK]はキャッシュ効率があまりよくありません。マップをstd::vector<pair<MyStruct,I*>>として実装することをお勧めし、そこでは[マップの代わりにバイナリ検索を使用しますルックアップ]は、少なくとも[要素の挿入を停止]のルックアップのみを開始すると効率がよくなります。std::vectormapよりもキャッシュに収まる可能性が高いためです。

この係数[cpu-cache]は通常、無視され、Big O表記では定数として隠されますが、大きなコレクションの場合、大きな影響を与える可能性があります。

4
amit

マップの使用方法は、MyStructインスタンスに基づいてルックアップを行うものであり、特定の実装によっては、必要な比較にコストがかかる場合とそうでない場合があります。

2
Johann Gerell

使用したい代替実装はありますか?私は自分で書くべきではないと思いますが、それが良い考えだと思ったら書けます。

問題を十分に理解している場合は、実装がどのように優れているかを詳しく説明する必要があります。

mapは適切な構造ですか?もしそうなら、あなたの標準ライブラリの実装はおそらく良質です(よく最適化されています)。

MyStruct比較を簡略化できますか?

問題はどこにありますか?調べる?

構造のコピーと割り当てのコストを最小限に抑えましたか?

1
justin

コメントで述べたように、適切なコードがなければ、あなたに与える普遍的な答えはほとんどありません。ただし、MyStructが非常に大きい場合、スタックのコピーにはコストがかかる可能性があります。おそらく、MyStructへのポインタを格納し、独自の比較メカニズムを実装することが理にかなっています。

template <typename T> struct deref_cmp {
  bool operator()(std::shared_ptr<T> lhs, std::shared_ptr<T> rhs) const {
    return *lhs < *rhs;
  }
};

std::map<std::shared_ptr<MyStruct>, I*, deref_cmp<MyStruct>> mymap;

ただし、これはプロファイルする必要があるものです。 可能性があるスピードアップ。

あなたはこのような要素を調べるでしょう

template <typename T> struct NullDeleter {
  void operator()(T const*) const {}
};
// needle being a MyStruct
mymap.find(std::shared_ptr<MyStruct>(&needle,NullDeleter()));

言うまでもなく、最適化する可能性はさらにあります。

1
bitmask