forループループwhileプログラミング言語、c ++/java?

パフォーマンスはどれが良いですか?これは他のプログラミング言語と一貫していない可能性があります。異なる場合、または特定の言語で自分の知識で私の質問に答えることができる場合は、説明してください。

私は例としてc ++を使用しますが、java、c、またはその他の主流言語でどのように動作するかを知りたいと思います。

int x = 0;
 while (x < 10) {
    cout << x << "\n ";
    x++;
  }

VS

for ( int x = 1; x < 10; x++)   
 cout << x << "\n ";

どちらが良いですか?それがforループであれば、whileループのインクリメントで使用できる宣言済みの整数があり、whileループのためだけに作成する必要はありませんでした。

例:

int age = 17; //this was made for something else in the code, not a while loop. But fortunately for us, our while loop just so happened to need the number 17.

 while (age < 25) {
        cout << age << "\n ";
        age++;
      }

このインスタンスはwhileループをforループを作成するよりも良い選択にしますか?私はこれに幾分類似した質問を見ましたが、私はこれが重複しているとは思わないか、他の質問に対する回答のいずれかが私の質問に答えました。

私は、コンパイラに固有のものか、それがどのように動作するか、あるいはこの質問に対する良い答えが何であるかを説明しながら、この質問の説明をしたい。

0
@Voo残念ながら私はどのアセンブリも知らない
追加された 著者 Gabriel,
@DavidHeffernanは答えを出す
追加された 著者 Gabriel,
この場合、生成されるassmebly/ILコードを調べなければなりません。ほとんどの場合、コンパイラは同じコードを生成しますが、他のいくつかのケースではいくつかの並べ替えだけが行われます。ここでは目立つ違いはありません。
追加された 著者 Voo,
@Gabe:アセンブリ言語ではありません。出力を見てみましょう。
追加された 著者 Hovercraft Full Of Eels,
これは最悪の場合には時期尚早の最適化です。 問題ではありません
追加された 著者 SLaks,
プロファイリングなしで最適化しないでください。あなたが書いた全コードの99.99%でこれらのループの間に違いは見られません。このようなものを最適化するつもりなら、少なくともアセンブリを見てください。これは、さまざまな種類のループで異なります。
追加された 著者 Nicol Bolas,
パフォーマンスの観点から、私は何かの違いが全くないことを非常に疑う。私は可読性についてもっと心配しています。
追加された 著者 NullUserException,
追加された 著者 Pubby,

9 答え

感知可能なコンパイラはすべて、ブランチとジャンプを含む同一のアセンブリ/ ILコードに対して同等のループをコンパイルする必要があります。 (少なくとも最適化を有効にして)

4
追加された
@パビー:どのように?どうして?
追加された 著者 SLaks,
それは少し上のレベルのステートメントです。私は間違っているかもしれませんが、それはコンパイラが最適化することをもっと熱望できることを意味します。
追加された 著者 Pubby,
必ずしも。コンパイラは、より強力なループを最適化することが知られています。
追加された 著者 Pubby,

特定のCPUアーキテクチャでは、 for for の選択よりも、ループの特性によって最適化の機会が増える可能性があります。

特に、 FORTRAN Pascal では、いくつかのCPUが最適化できるループ反復の回数に対して、変数ではなく定数を推奨していました。たとえば、サイバー(1970年代の Iron Dinosaur メインフレーム)では、15ビットレジスタに for ループインデックスがあり、簡単に比較できます。代わりに while は、アクセス困難な60ビットレジスタのうちの1つまたは2つを使用します。また、サイバー分岐命令はループハウスキーピングよりもかなり高価であり、おそらくループの内容である。そのような場合、高いレベルの最適化により、すべてのオーバーヘッドを避けるためにループが展開される可能性があります。

しかし、現代のコードジェネレータは、以前のように動作しません。ソースコードは、そのような選択肢を抽象化する中間解析構造に変わります。要するに、は違いがありません。

3
追加された

コードサンプルに異なるパフォーマンス特性がある状況を想像するのは難しいです。

私はあなたのために軽度の好奇心を持っています。パスカルのような言語(Delphiなど)では、ループの制限は1回だけ評価されます。これはループの制限が各反復で評価されるようなC言語とは異なります。これはパフォーマンスに影響を与えることができますが、ループの外側にローカルを導入することによって、C言語でパフォーマンスコードを書くのは簡単です。

例えば:

Delphi

for i := 0 to List.Count-1 do
  DoStuff(List[i]);

List.Count is only evaluated once.

C ++

for (int i=0; i

ここで、 List.getCount()はループのたびに呼び出されます。

ループの限界を評価するのが高価な場合は、この相違が関係する可能性があります。当然のことながら、ループ外で List.getCount()を評価し、その結果をローカル変数に格納することは自明です。

パスカルとC/C ++の for ループを比較すると、私はパスカルのバージョンが非常に単純であると言います。これは必ずしも悪いことではありません。なぜなら、より複雑な場合には常に while が利用できるからです。

2
追加された
@DavidHeffernan「私はあなたのために軽度の好奇心を持っていますが」とは、あなたが私について不思議なことを意味します。あなたが言っていることは、「私はあなたと同じ好奇心を共有している」などのようなものだと思います
追加された 著者 Gabriel,
あなたの最初のパラグラフ「私はあなたのために軽度の好奇心を持っています。
追加された 著者 Gabriel,
まあ実際に私のせいで、彼らの最初の使い方について頭字語を書いておきましょう。それは残念です。デルファイC ++コンパイラはそれを行い、特にJava JITは本当に好きです。しかし実際には、複雑なループのボディやメソッドを取得するとすぐに、特に信頼できる最適化ではないので、関数が高価な場合(複雑なメソッドを意味します)、手動で行う必要があります。しかし、 myArr.length()のようなものでは、通常はうまく動作します。
追加された 著者 Voo,
申し訳ありませんが、共通部分表現の削除基本的にコンパイラはループ本体で List が変更されていないことを保証しようとします。また、 getCount()には副作用がなく、プログラマが行うように。ほとんどの場合、例えば、 getCount()ですが、主にインライン展開と組み合わせて使用​​します。だから、実際に複雑なメソッドを手動で行うことはまだまだ道のりです。
追加された 著者 Voo,
興味深いデルファイ小説。あなたはおそらくその違いをもたらす場合は、CSEを言及する必要があります。また、ループスコープの外に変数を導入する必要はありません: for(int i = 0、max = List.GetCount(); i - 通常のイディオム私の経験ではC/C ++コードで。
追加された 著者 Voo,
そのコメントが何を意味するかわからないごめんなさい。
追加された 著者 David Heffernan,
@Voo CSEは何ですか?私はそれを知らないと嘆いています!
追加された 著者 David Heffernan,
いいえ、私が言っていることは、Delphiに関する楽しいことは好奇心だということです。私は個人としてあなたのための少しの好奇心を持っていると言うことが恐れている!
追加された 著者 David Heffernan,
私はそれに精通していた場合、私はそれを言及していただろう。でも僕はそうじゃない。私の経験は主にデルファイだから、私はこれまでに出会ったとは思っていません。デルファイが私には考えている最適化ではありません。
追加された 著者 David Heffernan,
@Gabe好奇心: "奇妙な、または珍しい物体や事実。" (二次定義)
追加された 著者 Aaron Dufour,

実際に投稿した例は、 while for の動作が異なります。

この:

for (int x = 0; x < 100; ++x) {
  foo y;
  y.bar(x);
}

Is equivalent to この:

{//extra blocks needed
  int x = 0;
  while (x < 100) {
    {
      foo y;
      y.bar(x);
    }
    ++x;
  }
}

And you can expect the performance to be identical. Without the extra braces the meaning is different, and so the assembly generated may be different.

両者の違いは現代のコンパイラには存在しませんが、 for はその動作をより明示的に表現しているので最適化されます。

2
追加された

コンパイラの最適化には何の違いもありません!

私の2ctsでは、ループ実装の選択については、アルゴリズムロジックに合ったものを使用することが重要です。コードを読むのが簡単です。

1
追加された

これはコンパイラ固有のものですが、ほぼすべての場合、両方のアプローチで同じ性能が得られると思います。

特定の状況を確認するには、パフォーマンスを測定する必要があります。このようなマイクロ最適化コードを何時間も費やす前に、まずアプリケーションのボトルネックであることを確認する必要があります。

2番目の例は、初期化式を持たない for ループとして記述することができます。

int age = 17;//This was made for something else in the code

for (; age < 25; age++) {
    cout << age << endl;
}
1
追加された

あなたの質問はあなたに与えられたときに病気です。あなたがループ内に持っているIOは、ループ処理ステートメントがこれまでに少なくとも1桁大きいことがあります。私の賭けは、IOが生成するノイズ(測定の点で)と区別できないため、大きな違いは測定できないことさえあります。

ループ内のコードが本当に速いのであれば、問題は重要です。ここでは、メモリ読出し命令を持っていなくても、すでにCPUメモリの帯域幅が支配的になっていることを意味します。

だからあなたが本当に好奇心が強いなら、アセンブラを読んでコードを見てください。それぞれのタイプのループだけで2つの小さな関数がある場合、出力はそれほど難しくありません。実際には、怖がらないでください。私はCでそれをすることをお勧めしますが、これはおそらくアセンブリに最も近く、部品を特定するのが最も簡単です。

あなたはあなたがどのシステム/コンパイラであるか教えてくれませんでした。 gccを使用すると、 .s ファイルを生成するオプションが -S -O3 -march = native になります。

1
追加された

私はあなたが小さな例を書いてその違いを時間を割くことを提案します。次に、最も迅速になるソリューションを選択します。

1
追加された

私はコンパイラが最近最適化されていると思うので、forループやwhileループを使用すると実際には違いはありません。

1
追加された
私は微妙な違いがあると確信しています
追加された 著者 Gabriel,