0

次のようなループが表示されたとします。

for(int i=0;
    i<thing.getParent().getObjectModel().getElements(SOME_TYPE).count();
    ++i)
{
  thing.getData().insert(
    thing.GetData().Count(),
    thing.getParent().getObjectModel().getElements(SOME_TYPE)[i].getName()
    );
}

これが Java の場合は、おそらくよく考えないと思います。しかし、C ++のパフォーマンスが重要なセクションでは、それをいじくり回したくなります...しかし、コンパイラがそれを無駄にするほど賢いかどうかはわかりません。これは架空の例ですが、コンテナに文字列を挿入しているだけです。これらのいずれもが STL 型であると想定しないでください。以下について一般的な用語で考えてください。

  • for ループ内の乱雑な条件は、毎回評価されるのでしょうか、それとも 1 回だけでしょうか?
  • これらの get メソッドが単にオブジェクトのメンバー変数への参照を返す場合、それらはインライン化されますか?
  • カスタム [] 演算子がまったく最適化されると思いますか?

言い換えれば、次のようなものに変換するのに時間をかける価値があります (パフォーマンスのみで、読みやすさではありません)。

ElementContainer &source = 
   thing.getParent().getObjectModel().getElements(SOME_TYPE);
int num = source.count();
Store &destination = thing.getData();
for(int i=0;i<num;++i)
{
  destination.insert(thing.GetData().Count(), source[i].getName());
}

これは 1 秒間に何百万回も呼び出されるタイトなループです。私が疑問に思っているのは、これらすべてがループごとに数サイクル削減されるのか、それとももっと実質的なものになるのかということです。


はい、「時期尚早の最適化」についての引用を知っています。そして、プロファイリングが重要であることを知っています。しかし、これは最新のコンパイラ、特に Visual Studio に関するより一般的な質問です。

4

6 に答える 6

4

このような質問に答える一般的な方法は、生成されたアセンブリを調べることです。gcc では、-cフラグをに置き換える必要があり-Sます。

私自身のルールは、コンパイラと戦わないことです。inline何かをインライン化する必要がある場合は、そのようなインライン化を実行するために必要なすべての情報がコンパイラーにあることを確認し、(おそらく) 明示的なキーワードを使用してそうするようにコンパイラーに促します。

また、インライン化によりいくつかのオペコードが節約されますが、コードが大きくなり、L1 キャッシュに関する限り、パフォーマンスが非常に低下する可能性があります。

于 2010-03-31T13:04:50.900 に答える
2

あなたが尋ねているすべての質問はコンパイラ固有のものであるため、唯一の賢明な答えは「依存する」です。それが重要な場合は、(いつものように) コンパイラが発行しているコードを見て、タイミングの実験を行う必要があります。コードがすべての最適化をオンにしてコンパイルされていることを確認してください。operator[]()これは、インライン関数として実装されることが多いが、最適化をオンにしない限り (少なくとも GCC では) インライン化されない などに大きな違いをもたらす可能性があります。

于 2010-03-31T13:05:01.197 に答える
1

原則として、ループの実行中に結果が変化する場合を除いて、「for条件」にすべてのガベージを含めるべきではありません。

ループ外で設定された別の変数を使用します。これにより、コードを読み取るときにWTFが排除され、パフォーマンスに悪影響を与えることはなく、関数がどの程度最適化されるかという問題が回避されます。これらの呼び出しが最適化されていない場合、パフォーマンスも向上します。

于 2010-03-31T14:12:50.077 に答える
1

ループがそれほど重要な場合は、生成されたコードを確認することをお勧めします。コンパイラが呼び出しを積極的に最適化することが許可されている場合、おそらく問題にはなりません。申し訳ありませんが、最新のコンパイラは非常にうまく最適化できるため、特定のケースで最適なソリューションを見つけるためにプロファイリングをお勧めします。

于 2010-03-31T13:04:31.250 に答える
1

メソッドが小さく、インライン化が可能であり、インライン化される場合、コンパイラは、実行したのと同じ最適化を実行する可能性があります。それでは、生成されたコードを見て比較してみましょう。

編集: const メソッドを としてマークすることも重要ですconst。たとえば、例でcount()は、これらのメソッドが指定されたオブジェクトの内容を変更しないことをコンパイラに知らせるgetName()必要があります。const

于 2010-03-31T13:05:56.610 に答える
0

この場合、コンパイラがアクセスできるコンパイル時の情報の範囲を考えると、合法的にできる以上のことをコンパイラに求めていると思います。そのため、特定のケースでは、厄介な状態が最適化されて取り除かれる可能性がありますが、実際には、コンパイラーには、関数呼び出しの長いチェーンからどのような副作用が生じる可能性があるかを知るための特に良い方法はありません. 別の方法で示すベンチマーク (または逆アセンブリ) がない限り、テストを分割する方が高速であると思います。

これは、JIT コンパイラーが C++ コンパイラーよりも大きな利点を持つケースの 1 つです。原則として、実行時に見られる最も一般的なケースを最適化し、そのために最適化されたバイトコードを提供できます (さらに、そのケースに該当するかどうかを確認するためのチェックも行います)。この種のことは、実際には多態的に使用されないことが判明した多態的なメソッド呼び出しで常に使用されます。ただし、あなたの例のように複雑なものをキャッチできるかどうかはわかりません。

価値があるのは、速度が本当に重要な場合は、Java でも分割することです。

于 2010-03-31T13:47:10.147 に答える