11

ちょっと変わった話: 友人forから、この例のループを次のように再配置するように言われました:

for(int i = 0; i < constant; ++i) {
    // code...
}

に:

for(int i = 0; constant > i; ++i) {
    // code...
}

C++ でのパフォーマンスがわずかに向上します。定数値と変数の比較がその逆よりも速いことはわかりません。実行したいくつかの基本的なテストでは、2 つの実装間で速度に違いは見られませんでした。whileこの Pythonループのテストでも同じことが言えます。

while i < constant:
    # code...
    i += 1

対:

while constant > i:
    # code...
    i += 1

私が間違っている?私の単純なテストでは速度変動を判断するのに十分ではありませんか? これは他の言語にも当てはまりますか?それとも、これは単なる新しいベスト プラクティスですか?

4

13 に答える 13

45

それは、特定のコンパイラの特定のバージョンで一度機能し、所有者を一般的な群れから区別するある種の伝承としてその後ずっと受け継がれる、C++ の民間伝承のラインにあり、手動のマイクロ最適化です。ゴミだ。プロファイリングは真実です。

于 2009-04-09T13:18:29.497 に答える
17

おそらくそうではありませんが、そうであれば、コンパイラはおそらく自動的に最適化を行うでしょう。したがって、コードを最も読みやすくする方法は何でも行ってください。

于 2009-04-09T13:16:33.183 に答える
10

私の疑いでは、あなたの友人は100%間違っています。でも、あなたの友人を信頼する以上に、自分の意見を信頼することはありません。実際、パフォーマンスの問題が発生した場合、信頼できる人物は 1 人しかいません。

プロファイラー

これは、ある方法が別の方法よりも高速である、または高速ではないということを権威に主張できる唯一の方法です。

于 2009-04-09T13:16:52.293 に答える
5

短絡評価を仮定すると、これが大きな違いを生むのは、ループ内で遅い関数を呼び出す場合だけです。たとえば、データベースから値を照会して返す関数がある場合、次のようになります。

while(bContinue && QueryStatusFromDatabase==1){
}  //while

以下よりもはるかに高速です。

while(QueryStatusFromDatabase==1 && bContinue){
}  //while

それらは論理的には同一ですが。

これは、最初のものは単純なブール値が FALSE になるとすぐに停止できるためです。クエリはブール値が TRUE の場合にのみ実行する必要がありますが、2 つ目は常にクエリを実行します。

可能なすべての CPU サイクルをループから絞り出す必要がない限り、これらの極端なケースだけに時間を費やす価値があります。このように考えてみてください。この質問に費やした時間を埋め合わせるには、おそらく数十億回のループの反復が必要になるでしょう。

最悪なのは、条件として関数があり、その関数に、コード内の別の場所で密かに期待される副作用がある場合です。したがって、ちょっとした最適化を行うと、副作用がたまにしか発生せずコードが奇妙な形で壊れてしまいます。しかし、それはちょっとした接線です。あなたの質問に対する短い答えは、「時々、しかし通常は問題ではありません」です。

于 2009-04-09T13:28:15.667 に答える
4

プロファイリングは最適ですが、唯一の方法ではありません。

各オプションが作成するアセンブリを比較できますが、このようなマイクロ最適化では問題外ではありません。ハードウェア プラットフォームのコマンドを少し調べてみると、この変更によって違いが生じるかどうか、およびパフォーマンスがどのように異なるかについて、適切なアイデアが得られる可能性があります。あなたの例では、移動数と比較コマンドを数えていると思います。

デバッガーで、ステップ実行中にソース ビューと逆アセンブル ビューを切り替えることができる場合、これは非常に簡単です。

于 2009-04-09T13:59:29.150 に答える
3

このような最適化の微調整のために邪魔にならないようにすることは、ベスト プラクティスです

于 2009-04-09T13:15:54.807 に答える
2

健全なコンパイラは、両方を同じ方法で実装します。あるアーキテクチャーで一方が他方よりも高速である場合、コンパイラーはそれをそのように最適化します。

于 2009-04-09T18:20:44.160 に答える
1

0 との比較は非常に高速であるため、これは実際にはわずかに高速です。

for (int i = constant; i > 0; --i)
{ 
  //yo
}

いずれにせよ使用する方が良いと思います!=.1つエラーを検出しやすくし、リンクされたリストのような不連続なデータ構造でイテレータを使用する唯一の方法です.

于 2009-08-03T06:15:04.397 に答える
0

特定のアーキテクチャの一部のコンパイラでは、バリアントよりも効果的に以下を削減できる可能性があることを謙虚に提案します。

i = constant - 1
while (--i) {
}

一定の反復を取得します。

多くのコメントが示唆しているように、コンパイラーはループを最適化してくれます (コンパイラーの最適化については、多くの時間を費やして検討してきました)。判読可能なコードはおそらくより価値がありますが、YMMV!

コンパイラができると考えている以上の最適化が本当に必要な場合は、高級言語が生成するアセンブリを調べて、そこからさらに最適化を検討することをお勧めします。

高レベルでは、OpenMP を使用するか、低レベルではベクトル命令セット (MMX など) を使用して 1 つの命令で複数の計算を実行することにより、パフォーマンスを大幅に向上させることもできます。それは質問の範囲を少し超えています。その上で役立つアドバイスを得るには、ループが何をしているのかについてさらに多くの情報を提供する必要があります。

それが助けて乾杯することを願っています。

于 2009-04-09T15:17:58.593 に答える
0

提供された最適化は、特定のコンパイラに対してのみ最適化します (おそらく)。抽象的には、同じコードを生成する必要があります。

マイクロ最適化を行う場合 (マイクロ最適化を行うための要件が​​満たされていると仮定)、最初のステップは、生成されたアセンブリを確認し、次にアーキテクチャのアセンブリ マニュアルを確認することです。

たとえば、i++ は i+1 よりも高速な場合があります。依存します。単純な CPU では、0 に等しくすることは、より小さいよりもはるかに高速です。コンパイラ/CPU が命令の並べ替えをサポートしていない場合は、計算に代入を散在させるとコードが高速化されることがあります。(特定の計算はパイプラインのストールを引き起こす可能性があります) しかし、それはコンパイラとアーキテクチャの組み合わせについて具体的に決定する必要があるものです。

率直に言って、プロセッサからの最後のサイクルをすべて絶対に必要としない限り、このレベルの最適化をわざわざ行うことはありません。伝統的に、グラフィックスや科学計算は、この種のものを必要とする場所です[*]。

*数か月の最適化の後、最新のマシンでデータを処理するのにまだ何か月もかかるプログラムを知っています。1 つのデータセットの実行時間は週の範囲です。使用するデータはかなりあります....

于 2009-07-31T20:46:18.820 に答える
-1

これは完全にマイクロ最適化のケースであり、実際に行う必要はありません。

(特に) C++ では、ポストインクリメント操作とプレインクリメント操作の間にわずかなパフォーマンスの違いがあることは事実ですが、今日のコンパイラでのその違いは一般的に無視できます。条件の順序を変更する理由は、ポストインクリメントからプリインクリメントへの変更によるものです。

于 2009-04-09T13:18:47.407 に答える