どのように最速の反復を可能にするいくつかの計算集中的なコードですか?

コンテキスト

QLinkedList を使って、私が書いたクラスを保存しています。
実際、このリストをたくさん繰り返す必要があります。
多くの場合、私が書いたプログラムは無限の計算を行います(あなたはまだそれを手動で止めることができます)。そして、各繰り返しの QLinkedList を調べる必要があります。

問題

The 問題 is not if I'm iterating to much over this list.

それは私のコードをプロファイリングしているので、1/4の時間はQLinkedList :: end()と QLinkedList :: begin()関数に費やされていることがわかります。

サンプルコード

私のコードは次のとおりです:

typedef QLinkedList ParticlesList; //Particle is a custom class

ParticlesList* parts =//assign a QLinkedList

for (ParticlesList::const_iterator itp = parts->begin(); itp != parts->end(); ++itp)
{
    //make some calculus
}

Like I said, this code is called so often that it spends a lot of time on parts->begin() and parts->end().

質問

So, the 質問 is how can I reduce the time spent on the iteration of this list ?

可能な解決策

ここで私が考えたいくつかのソリューションは、私が最高を選択するか、私に別のものを提案するのを助けてください:)

  • 古典的なC配列の使用://このミスで申し訳ありません
Particle** parts =//assing it something
for (int n = 0; n < LENGTH; n++)
{
    //access by index
    //make some calculus
}

これはすぐにすべきですか?

  • Javaスタイルのイテレータを使用している可能性がありますか?
  • 別のコンテナを使用している可能性がありますか?
  • Asm?ちょうど冗談...または多分?

今後のお返事ありがとうございます!

PS:プロファイルを作成するタイミングについては、stackoverflowの投稿を読んでいますので、心配しないでください;)

編集:

リストが変更されました

私は最も重要なことを忘れてしまったと思って申し訳ありません、私はストップせずに関数全体を記述します:

typedef std::vector Neighbours;
typedef QLinkedList ParticlesList;

Neighbours neighbours = m_cell->getNeighbourhood();
Neighbours::const_iterator it;

for (it = neighbours.begin(); it != neighbours.end(); ++it) 
{
    ParticlesList* parts = (*it)->getParticles();
    for (ParticlesList::const_iterator itp = parts->begin(); itp != parts->end(); ++itp)
    {
        double d = distanceTo(*itp);//computes sqrt(x^2 + y^2)
        if(d>=0 && d<=m_maxForceRange)
        {
            particleIsClose(d, *itp);//just changes 
        }
    }
}

私が完全であることを確認するために、このコード全体がループで呼び出されます^^

So yes リストが変更されました and it is in a inner loop. So there's no way to precompute the beginning and end of it.

さらに、リストは、1つずつ挿入することによって、大きな反復ごとに構築する必要があります(一番上のループを意味します)。

デバッグモード

Yes indeed I profiled in デバッグモード. And I think the remark was judicious because the code went 2x faster in Release. And the 問題 with lists disappeared.

あなたの答えに感謝し、このことを申し訳ありません^^

3
追加された 著者 Mike Dunlavey,
リストにはいくつのアイテムがありますか?たとえば、1つのアイテムがある場合は、 begin()、2 end()と1つの QLinkedList :: iterator :: operator ++ ループが繰り返されるたびに...リストが実際に long の場合は、 begin()end()</(ループの本体がほとんど空でない限り)コストが最も高くなります。連続したメモリには std :: vector を使用し、リリースモードでは最適化でコンパイルすることを検討してください。通常は
追加された 著者 David Rodríguez - dribeas,
これは「古典的なC ++配列」ではなく、古典的なC配列です。 C ++では、 std :: vector を使用します。
追加された 著者 GManNickG,
とにかく、ループを開始するとリストが変わるのですか、ループ中に要素を挿入できますか?
追加された 著者 GManNickG,
あなたは常にリストのすべてのアイテムを処理する必要がありますか?あなたはループの中でいくつかの欲望をしていますか?
追加された 著者 vz0,

4 答え

デバッグモードでプロファイリングする場合、多くのコンパイラがインライン化を無効にします。高くなっているbegin()とend()の時間は「実際」ではないかもしれません。メソッド呼び出し時間は、同等のインライン操作よりもはるかに高いでしょう。

他の何か、私は完全なコードで気づいた、あなたは内側のループでsqrtをやっている。ハードウェアのアーキテクチャによってはかなり高価になる可能性があります。私は次のコードを置き換えることを検討します:

 double d = distanceTo(*itp);//computes sqrt(x^2 + y^2) 
 if(d >= 0 && d <= m_maxForceRange) 

with:

 double d = distanceToSquared(*itp);//computes x^2 + y^2
 if(d >= 0 && d <= m_maxForceRangeSquared)

私はコリソン検出を行っているコードでこれを行いましたが、ときに目立った改善が見られます。テストは同等で、sqrtへの呼び出しをたくさん保存します。常に最適化と同様に、速度を改善するかどうかを測定します。

5
追加された

コンパイラがそれがconstであることを認識するのに十分なほどスマートではない場合、ループを通過するたびにそれを計算している場合、エンドイテレータを事前に計算すると役立ちます。あなたは以下のようにすることができます:

const ParticlesList::const_iterator itp_end = parts->end();

for (ParticlesList::const_iterator itp = parts->begin(); itp != itp_end; ++itp)
{
    //make some calculus
}

I can't understand why parts->begin(); is taking so long, it should only be used once. However, if this loop is inside another loop, you could do something like this:

const ParticlesList::const_iterator itp_begin = parts->begin();
const ParticlesList::const_iterator itp_end = parts->end();

for (...)
{
  for (ParticlesList::const_iterator itp = itp_begin; itp != itp_end; ++itp)
  {
      //make some calculus
  }
}

しかし、私はこれが(あなたの内側のリストが本当に短い場合を除いて)あまりにも多くの違いを生み出すとは想像できませんが、どちらもそれほど傷ついてはいけません。

さらに注意すべき点として、リンクされたリストは、おそらくあなたの目的にとって最速のデータ構造ではありません。リンクされたリストは、頻繁にアイテムをリストの中央に挿入する必要がある場合に最も便利です。リストがビルドされてから修正されたら、 std :: vector を使う方がよいでしょう。場合によっては最後から項目を追加/削除する必要がある場合でも、 std :: vector はより良いかもしれません。最初から最後まで追加/削除しなければならない場合は、 std :: deque を考慮してください。

2
追加された
いい答えです。また、ホイストされたイテレータは constconst ParticlesList :: const_iterator itp_end = ... )で作成する必要があります。
追加された 著者 Drew Hall,

生の速度が絶対に必要な場合は、遭遇する可能性のある選択肢をそれぞれ測定し、最速に保つ必要があります。

リストのようなサウンドは、繰り返し処理している間は変更されません。リストの終わりをローカル変数に格納してみることにしました。

typedef QLinkedList ParticlesList; //Particle is a custom class

ParticlesList* parts =//assign a QLinkedList
ParticlesList::const_iterator end = parts->end();

for (ParticlesList::const_iterator itp = parts->begin(); itp != end; ++itp)
{
   //make some calculus
}
1
追加された
このようにすることもできます: begin()、end = parts-> end(); ... 。 for-loopスコープ
追加された 著者 GManNickG,

Qtコンテナは std :: for_each のようなSTLアルゴリズムと互換性があります。

次のようなことを試してください:

std::for_each( parts->begin(), parts->end(), MyParticleCalculus );

ここで MyParticleCalculus はあなたの計算を含むファンクタです。

Qtも独自の foreach を持っていますが、イテレータを隠すだけのマクロなので、パフォーマンス上の利点はありません。

(編集: "効果的なSTL"のScott Meyerの推薦に従って、 std :: for_each を推薦しています: "手書きループへのアルゴリズム呼び出しを優先してください。 "

1
追加された