どちらが速いですか:while(1)またはwhile(2)?


587

これは上級管理職からのインタビューの質問でした。

どちらが速いですか?

while(1) {
    // Some code
}

または

while(2) {
    //Some code
}

内部の式whileは最終的にtrueまたはに評価されるため、どちらも実行速度は同じであると述べましたfalse。この場合、どちらも評価されtruewhile条件内に追加の条件付き命令はありません。したがって、両方の実行速度は同じであり、私は(1)を優先します。

しかし、インタビュアーは自信を持って言った:「あなたの基本を確認してください。while(1)より速いですwhile(2)。」(彼は私の自信をテストしていませんでした)

これは本当ですか?

参照:「for(;;)」は「while(TRUE)」より高速ですか?そうでない場合、なぜ人々はそれを使うのですか?


202
半分まともなコンパイラーは、両方の形式を何にも最適化しません。

64
最適化ビルドのすべてのwhile(n)では、n!= 0またはfor(;;)は、最初にラベル、最後にgotoを含むアセンブリの無限ループに変換されます。まったく同じコード、同じパフォーマンス。
アレックスF 14

60
当然のことですが、株式最適化は両方のスニペット0x100000f90: jmp 0x100000f90に(アドレスは明らかに異なります)もたらします。インタビュアーは、レジスターテストと単純なフラグ付きジャンプを回避した可能性があります。問題とその仮定はどちらも不十分です。
WhozCraig 14

50
インタビュアーによるこの質問は、dilbert.com / strips / comic / 1995-11-17と同じ支持の下にあり ます-あなたは、彼らの陳述における愚かさの商に関係なく、彼らが言っていることを本当に信じている誰かに会います。単に以下から選択してください:深いブリート、誓う、笑う、泣く、上記のいくつかの組み合わせ:)
GMasucci

3
@Mike W:コンパイラーが何をすべきか不思議に思うかもしれません:Haltステートメントに変換するか、無限時間後にループが終了して無限遅延を最適化することを検討してください?
Yves Daoust、2014

回答:


681

両方のループは無限ですが、反復ごとにどちらがより多くの命令/リソースを必要とするかを確認できます。

gccを使用して、次の2つのプログラムをコンパイルして、さまざまなレベルの最適化でアセンブリしました。

int main(void) {
    while(1) {}
    return 0;
}


int main(void) {
    while(2) {}
    return 0;
}

最適化を行わなくても(-O0)、生成されたアセンブリは両方のプログラムで同一でしたしたがって、2つのループ間に速度の違いはありません。

参考までに、生成されたアセンブリを以下に示します(gcc main.c -S -masm=intel最適化フラグを使用)。

-O0

    .file   "main.c"
    .intel_syntax noprefix
    .def    __main; .scl    2;  .type   32; .endef
    .text
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    push    rbp
    .seh_pushreg    rbp
    mov rbp, rsp
    .seh_setframe   rbp, 0
    sub rsp, 32
    .seh_stackalloc 32
    .seh_endprologue
    call    __main
.L2:
    jmp .L2
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

-O1

    .file   "main.c"
    .intel_syntax noprefix
    .def    __main; .scl    2;  .type   32; .endef
    .text
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    sub rsp, 40
    .seh_stackalloc 40
    .seh_endprologue
    call    __main
.L2:
    jmp .L2
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

-O2し、-O3(同じ出力):

    .file   "main.c"
    .intel_syntax noprefix
    .def    __main; .scl    2;  .type   32; .endef
    .section    .text.startup,"x"
    .p2align 4,,15
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    sub rsp, 40
    .seh_stackalloc 40
    .seh_endprologue
    call    __main
.L2:
    jmp .L2
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

実際、ループ用に生成されたアセンブリは、最適化のすべてのレベルで同じです。

 .L2:
    jmp .L2
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

重要なビットは:

.L2:
    jmp .L2

アセンブリをうまく読み取ることができませんが、これは明らかに無条件ループです。このjmp命令は.L2、値をtrueと比較することさえせずに、無条件にプログラムをラベルにリセットします。もちろん、プログラムが何らかの形で終了するまで、すぐに再びリセットします。これはC / C ++コードに直接対応します。

L2:
    goto L2;

編集:

興味深いことに、最適化を行わなくても、次のループはすべてjmpアセンブリでまったく同じ出力(無条件)を生成しました。

while(42) {}

while(1==1) {}

while(2==2) {}

while(4<7) {}

while(3==3 && 4==4) {}

while(8-9 < 0) {}

while(4.3 * 3e4 >= 2 << 6) {}

while(-0.1 + 02) {}

そして私の驚きにも:

#include<math.h>

while(sqrt(7)) {}

while(hypot(3,4)) {}

ユーザー定義関数を使用すると、少し面白くなります。

int x(void) {
    return 1;
}

while(x()) {}


#include<math.h>

double x(void) {
    return sqrt(7);
}

while(x()) {}

-O0、これらの2つの例が実際に呼び出さxれ、各反復の比較が実行されます。

最初の例(1を返す):

.L4:
    call    x
    testl   %eax, %eax
    jne .L4
    movl    $0, %eax
    addq    $32, %rsp
    popq    %rbp
    ret
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

2番目の例(を返すsqrt(7)):

.L4:
    call    x
    xorpd   %xmm1, %xmm1
    ucomisd %xmm1, %xmm0
    jp  .L4
    xorpd   %xmm1, %xmm1
    ucomisd %xmm1, %xmm0
    jne .L4
    movl    $0, %eax
    addq    $32, %rsp
    popq    %rbp
    ret
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

ただし、 -O1それ以降は、どちらも前の例と同じアセンブリを生成します(jmp前のラベルに無条件に戻る)。

TL; DR

GCCでは、さまざまなループが同じアセンブリにコンパイルされます。コンパイラーは定数値を評価し、実際の比較を実行する必要はありません。

物語の教訓は:

  • C ++ソースコードとCPU命令の間には変換の層があり、この層はパフォーマンスに重要な影響を与えます。
  • したがって、ソースコードだけを見てパフォーマンスを評価することはできません。
  • コンパイラーは、そのような些細なケースを最適化するのに十分スマートでなければなりません。プログラマー、大多数のケースでそれらについて考えることに時間を無駄にすべきではありません

196
インタビュアーがgccを使用していなかったのかもしれません
MM

106
@マットマクナブそれは良い点ですが、インタビュアーがコンパイラ固有の最適化に依存している場合、質問でそれについて非常に明確にする必要があり、「違いはない」という答えを正しいものとして受け入れる必要があります一部(ほとんど?)のコンパイラ。
ジョナサンハートレイ

109
疑いを取り除くために、私はこれをclang 3.4.2でテストしました。両方のループがすべての-Oレベルで同じアセンブリを生成します。
Martin Tournoij 14

16
ループの条件セクションに配置したものはすべてコンパイル時の定数であるため、これは驚くべきことではありません。そのため、コンパイラーは、ループが常にtrueまたはfalseであり、敬意を表して単純jmpに最初に戻るか、ループを完全に削除することをコンパイラーが認識すると思います。
sherrellbc 14

10
@hippietrailループの内容(またはその欠如)がこれらの最適化にどのように影響するか(breakステートメントの可能性を除いて)はわかりませんが、とにかくテストしましたが、ループ内のコードでさえ、ジャンプは絶対的との両方のための無条件while(1)while(2)。あなたが実際に心配している場合は、自分で他の人を自由にテストしてください。
DarknessFishへのアプローチ

282

はい、 while(1)はるかに高速よりもwhile(2)人間が読むことのために!見たらwhile(1)見慣れないコードベースと、その作者の意図がすぐにわかり、私の眼球は次の行に進むことができます。

表示された場合はwhile(2)、おそらく自分のトラックを停止して、その作者がなぜ書いていないのかを理解しようとしますwhile(1)。作者の指がキーボードの上で滑っていましたか?このコードベースのメンテナーは、while(n)ループを別の見た目にするための不明瞭なコメントメカニズムとして使用していますか?壊れた静的分析ツールの偽の警告に対する粗雑な回避策ですか?それとも、生成されたコードを読んでいる手がかりですか?それは、不適切な助言を受けた「検索と置換」、または不適切なマージ、または宇宙線によるバグですか?たぶん、このコード行は劇的に異なる何かをすることになっています。たぶんそれを読むことになっていたのwhile(w)while(x2)。ファイルの履歴から著者を見つけて「WTF」メールを送信した方がいいです。while(2)while(1) ほんの一瞬で済みます!

私は誇張していますが、ほんの少しです。コードの可読性は本当に重要です。そして、それはインタビューで言及する価値があります!


私の考えは、正確に、そしてなぜ(1)の方が速いのかについてです笑私は、マネージャーがコーディングについて知っていることを知らないことを彼が納得させようとしている古いケースだったと思いますが、誰もがそれを見て、誰でもなりたいと思っていますジェディ。
ekerner 2014

8
もちろん、これは決して誇張ではありません。間違いなくsvn-annotate or git blame(または何でも)がここで使用され、一般にファイル非難履歴をロードするのに数分かかります。それから、ついに「私が理解しているように、著者は高校を卒業したときにこの行を書いた」と判断するために、10分
遅れました

11
私は慣習にうんざりしているので、反対票を投じました。開発者は自分が好きな数字を書くというこの面倒な機会があり1ます。そうすると、退屈な誰かがやって来て、なぜそうでないのかについて悩みます。
Utkan Gezer 2014

1
修辞学により反対投票。while(1)の読み取りは、while(2)とまったく同じ速度です。
hdante 2014年

4
1分前にwhile(2)コードで見たとき、この回答で説明されているのとまったく同じ精神プロセスを経験しました(「このコードベースのメンテナーは、while(n)をあいまいなコメントメカニズムとして使用して、ループを異なったものにするために使用しますか?」 )。いいえ、あなたは誇張ではありません!
Hisham HM

151

特定のオプションセットを使用して特定のターゲットに対して特定のコンパイラによって生成されたコードを示す既存の回答は、その特定のコンテキストで質問が行われない限り、質問に完全には答えません(「x86_64でgcc 4.7.2を使用すると、どちらがより高速になりますか?デフォルトのオプションを使用しますか?」など)。

言語定義に関する限り、抽象マシンで while (1)は整数定数1while (2)評価し、整数定数を評価し2ます。どちらの場合も、結果はゼロと等しいかどうか比較されます。言語標準は、2つの構造の相対的なパフォーマンスについてはまったく何も述べていません。

非常にナイーブなコンパイラーが、少なくとも最適化を要求せずにコンパイルした場合、2つの形式に対して異なるマシンコードを生成する可能性があると想像できます。

一方、Cコンパイラは、定数式を必要とするコンテキストで表示される場合、コンパイル時に一部の定数式を評価する必要があります。たとえば、これ:

int n = 4;
switch (n) {
    case 2+2: break;
    case 4:   break;
}

診断が必要です。遅延コンパイラには、評価を2+2実行時まで延期するオプションはありません。コンパイラーは、コンパイル時に定数式を評価する機能を備えている必要があるため、不要な場合でもその機能を利用しない理由はありません。

C標準(N1570 6.8.5p4)はそれを言っています

反復ステートメントにより、ループ本体と呼ばれるステートメントが、制御式が0と比較されるまで繰り返し実行されます。

したがって、関連する定数式は1 == 0andであり2 == 0、どちらもint値に評価されます0。(これらの比較はwhileループのセマンティクスで暗黙的です。実際のC式としては存在しません。)

ひどくナイーブなコンパイラ、2つの構成体に対して異なるコードを生成する可能性があります。たとえば、最初の場合は無条件の無限ループ(1特別なケースとして扱う)を生成し、2番目の場合はと同等の明示的な実行時比較を生成でき2 != 0ます。しかし、実際にそのように動作するCコンパイラに出会ったことはありません。そのようなコンパイラが存在することを真剣に疑っています。

ほとんどのコンパイラー(私はすべての製品品質のコンパイラーと言いたくなります)には、追加の最適化を要求するオプションがあります。そのようなオプションの下では、コンパイラーが2つの形式に対して異なるコードを生成する可能性はさらに低くなります。

コンパイラが2つの構成要素に対して異なるコードを生成する場合は、最初に、異なるコードシーケンスが実際に異なるパフォーマンスを持っているかどうかを確認します。その場合は、最適化オプション(利用可能な場合)を使用して再度コンパイルしてみてください。それでも異なる場合は、コンパイラベンダーにバグレポートを送信してください。これは(必ずしも)C標準に準拠していないという意味でのバグではありませんが、ほぼ間違いなく修正すべき問題です。

結論:while (1)そしてwhile(2) ほぼ確実に同じパフォーマンスを持っています。それらはまったく同じセマンティクスを持ち、コンパイラーが同一のコードを生成しないという正当な理由はありません。

コンパイラがのwhile(1)よりも高速なコードを生成することは完全に合法ですが、コンパイラが同じプログラム内の別のオカレンスよりもwhile(2)高速のコードを生成することも同様に合法です。while(1)while(1)

(あなたが尋ねた質問には暗黙の別の質問があります:不適切な技術的ポイントを主張するインタビュアーにどのように対処しますか。それはおそらくWorkplaceサイトにとって良い質問でしょう)。


8
「この場合、関連する(暗黙の)定数式は1!= 0と2!= 0であり、どちらもint値1に評価されます」...これは非常に複雑で不正確です。標準では、制御式はwhileスカラー型である必要があり、式が0と比較されるまでループ本体が繰り返されると単純に規定されていますexpr != 0。それ-0または1-は0と比較され、無限に比較されます。いいえ、式は0と比較されますが、その比較では値が生成されません。PS私は賛成した。
ジムバルター2014

3
@JimBalter:私はあなたの要点を理解し、それを解決するために私の答えを更新します。しかし、私が意味したのは、標準の表現 "...制御式が0と比較されるまでは、評価することを意味し<expr> == 0ます。これが、Cで「0に等しい」という意味です。この比較は、whileループのセマンティクスの一部です。標準でも私の回答でも、結果を0再度比較する必要があるという意味はありません。(私が書かれているはず==ではなく!=。)しかし、私の答えの一部が不明であった、と私はそれを更新します。
キース・トンプソン

1
「「0に等しい」はCを意味します」-ただし、その言語は標準ではなくCです...実装は比較を行い、C言語の比較を生成しません。「どちらもint値1に評価される」と書いたが、そのような評価は行われません。あなたがのために生成されたコードを見るとwhile (2)while (pointer)while (9.67)、最も素朴な、最適化されていないコンパイラによって、あなたは利回りが生成するコードは表示されません0かを1。「私は!=ではなく==と書くべきでした-いいえ、それは意味がありませんでした。
ジムバルター2014

2
@JimBalter:うーん。「0に等しい比較」が... == 0C式の存在を意味することを示唆するつもりはありません。私の要点は、whileループの標準の説明に必要な「0に等しい比較」と明示的なx == 0式の両方が論理的に同じ演算を意味するということです。そして、私は実際のコンパイラーがそれほど素朴だとは思いませんが、痛みを伴うほど素朴なCコンパイラーが、ループのint0または1for whileループを生成するコードを生成する可能性があると思います。
キース・トンプソン


141

ちょっと待って。インタビュアー、彼はこの男のように見えましたか?

ここに画像の説明を入力してください

面接担当者自身がこの面接に失敗のは残念ですがこの会社の他のプログラマがこのテストに「合格」した場合はどうなるでしょうか。

いいえ文を評価1 == 0し、2 == 0 あるべき均等に速いです。私たちは、想像できる 1が速く、他のよりもあるかもしれない貧しいコンパイラの実装を。しかし、一方が他方よりも高速である必要があるという正当な理由はありません。

主張が真実である場合にあいまいな状況があっても、プログラマーはあいまいな(この場合は気味の悪い)雑学の知識に基づいて評価されるべきではありません。このインタビューについて心配する必要はありません。ここでの最善の行動は、立ち去ることです。

免責事項:これはオリジナルのディルバートの漫画ではありません。これは単なるマッシュアップです。


6
質問への回答含まれているとしたら、これはおかしいでしょう。
コーディグレイ

いや、本当は、真面目な会社が作成したすべてのコンパイラーが妥当なコードを生成することは、かなり簡単に想像できます。「最適化されていないケース」/ O0を取り上げましょう。多分、anatolygが投稿したようになるでしょう。それからそれはCPUの問題です、cmpオペランドは2から0よりも1から0に比べて少ないサイクルで実行されますか?一般にcmpを実行するには何サイクルかかりますか?ビットパターンに応じて可変ですか?速度が遅くなる「複雑な」ビットパターンcmpですか?個人的にはわかりません。ランク0からn(たとえばn = 31)までビットごとにチェックする非常に馬鹿げた実装を想像できます。
v.oddou 14

5
これも私のポイントです。cmpオペランド 1と200で等しく高速でなければなりません。おそらく、これが当てはまらないばかげた実装を想像できます。しかし、私たちはよりも高速な非イディオティックな実装を想像できますか?同様に、いくつかの先史時代に、利用可能な実装がそのようなばかげたものだった場合、今日それについて大騒ぎする必要がありますか?私はそうは思わない、これは先のとがった髪のボスの話であり、それは本当の逸品です!while(1)while(200)
janos 14

@ v.ouddou "cmpオペランドは、2から0に比べて1から0に比べて少ないサイクルで実行されますか-いいえ。サイクルとは何かを学習する必要があります。「私には個人的にはわかりません。ランク0からnまでビットごとにチェックする非常にばかげた実装を想像することができます」-または逆に、インタビュアーを無知な馬鹿者にしている。そして、なぜ少しずつチェックすることを心配するのですか?実装は、プログラムを評価している最中に昼休みを取ることを決定したボックスに入った人である可能性があります。
ジムバルター2014

79

あなたの説明は正しいです。これは、技術的な知識に加えて、自信をテストする質問のようです。

ちなみに、答えたら

どちらのコードも完了までに無限の時間がかかるため、どちらのコードも同等に高速です。

面接官は言うでしょう

ただし、while (1)1秒あたりの反復回数を増やすことができます。理由を説明できますか?(これはナンセンスです。もう一度自信を試す)

だから、あなたがしたように答えることによって、あなたはそうでなければこの悪い質問を議論することに無駄になる時間を節約できました。


これは、私のシステム(MS Visual Studio 2012)のコンパイラーによって生成された最適化をオフにしたコード例です。

yyy:
    xor eax, eax
    cmp eax, 1     (or 2, depending on your code)
    je xxx
    jmp yyy
xxx:
    ...

最適化をオンにすると:

xxx:
    jmp xxx

したがって、生成されたコードは、少なくとも最適化コンパイラを使用すれば、まったく同じです。


28
このコードは、コンパイラーが私のシステムに出力するものです。私はそれを補いませんでした。
anatolyg 14

10
icepack「whileのオペランドはブール型です」-まったくナンセンスです。あなたはインタビュアーですか?そのような主張をする前に、C言語とその標準に慣れることをお勧めします。
ジムバルター2014

29
「私はそれを補いませんでした。」-ナンセンスな話をしているアイスパックには気を付けないでください。Cにはブール型がなく(stdbool.hには_Boolがありますが、これは同じwhilewhileはなく、その前にlong のセマンティクスがあります)、のオペランドはブール型でも_Boolでも他の特定の型でもありません。のオペランドは任意の式にwhileすることができます... whileは0で中断し、0以外で続行します。
ジムバルター、2014

36
「どちらのコードも完了するまでに非常に長い時間がかかるため、どちらのコードも同等に高速です」と、興味深いことに思い当たりました。無限ループを終了する唯一の方法は、荷電粒子またはハードウェアの故障によってビットが反転するwhile (00000001) {}ことwhile (00000000) {}です。ステートメントをからに変更します。真のビットが多いほど、値が偽にフリップする可能性が低くなります。悲しいことに、2にも1ビットしかありません。ただし、3の場合、実行時間が大幅に長くなります。これは、常にこれを常に最適化するとは限らないコンパイラー(VC ++)にも適用されます。
ジョナサンディキンソン

8
@ mr5いいえ。ビットフリップが実際にこのような結果になるためには、何万年もの実行時間について話しています。単なる思考実験。不滅の種族から出てきた場合、-1を使用して、ビットフリッピングがプログラムに影響を与えないようにすることができます。
ジョナサンディキンソン

60

質問の最も可能性の高い説明は、インタビュアーが、プロセッサーがゼロ以外の値に到達するまで、プロセッサーが数値の個々のビットを1つずつチェックすると考えることです。

1 = 00000001
2 = 00000010

「ゼロですか?」アルゴリズムは数値の右側から始まり、ゼロ以外のビットに到達するまで各ビットwhile(1) { }をチェックする必要がありwhile(2) { }ます。ループは、ループごとに反復ごとに2倍のビットをチェックする必要があります。

これには、コンピューターがどのように機能するかについて非常に間違ったメンタルモデルが必要ですが、独自の内部ロジックがあります。確認する1つの方法は、以下の場合に依頼するだろうwhile(-1) { }while(3) { }場合も同様に速いだろうか、while(32) { }となり さらに遅いです


39
インタビュアーの誤解は、「2は条件式で使用するためにブール値に変換する必要があるintですが、1はすでにブール値です」のようなものになると思いました。
Russell Borogove 14

7
そして、比較アルゴリズムが左から始まっている場合、それは逆です。
パウロEbermann

1
+1、それはまさに私が考えたことです。基本的cmpに、CPU のアルゴリズムは最初の違いでの早期ループ終了を伴う線形ビットチェックであると誰かが信じていると完全に想像できます。
v.oddou 2014

1
@PeterCordes「gccやclangの-O0出力が文字通り不十分であると主張する(あなた以外の)誰も聞いたことがありません。」私はそのようなことを一度も言わなかったし、gccやclangをまったく考えていなかった。あなたは私を誤解しているに違いありません。MSVCが生み出すものが陽気であるというのは、あなたの意見ではありません。
blubberdiblub

1
@PeterCordes私は間違っていました。MSVCではwhile(1)、ループの前ではなく、式のブレークポイントが壊れています。しかし、式を離れて最適化すると、ループ内で折りたたまれます。休憩をたくさん入れてこのコードを見てください。最適化されていないデバッグモードでは、これを取得ます。最適化すると、多くのブレークポイントが一緒に落ちるか、次の関数(IDEがデバッグ中に結果のブレークポイントを表示します)に波及します- 対応する逆アセンブリ
blubberdiblub

32

もちろん、私はこのマネージャーの本当の意図を知りませんが、まったく別の見方を提案します。新しいメンバーをチームに雇うとき、彼が対立する状況にどのように反応するかを知ることは役に立ちます。

彼らはあなたを紛争に追いやった。これが本当なら、彼らは賢く、質問は良かった。銀行などの一部の業界では、問題をスタックオーバーフローに投稿することが拒否の理由になる場合があります。

もちろん、私にはわかりません。1つのオプションを提案するだけです。


それは確かに素晴らしいですが、while(2)vs while(1)は明らかにディルバートコミックから取られています。それは誰かが彼の正しい心の中で発明することはできません(とにかく誰かがwhile(2)をどのようにして考えて書くことができるものとして思いつくのでしょうか?)。あなたの仮説が真実なら、間違いなくあなたはそれをググることができるほどユニークな問題を与えるでしょう。「while(0xf00b442)はwhile(1)より遅い」のように、銀行はどのようにして調査対象者の質問を見つけるでしょうか?彼らはNSAであり、keyscoreにアクセスできると思いますか?
v.oddou 2014

25

手がかりは「上級管理職からの質問」にあると思います。彼がマネージャーになったとき、この人は明らかにプログラミングをやめて、それから彼/彼女がシニアマネージャーになるのに数年かかりました。プログラミングへの興味を失ったことはありませんが、当時からラインを書いたことはありません。ですから、彼の言及は、いくつかの回答が言及しているように「そこそこのコンパイラ」ではなく、「この人が20〜30年前に使用したコンパイラ」です。

当時、プログラマーはかなりの割合の時間を費やして、「中央のミニコンピューター」のCPU時間は非常に価値があるため、コードをより高速かつ効率的にするためのさまざまな方法を試しました。コンパイラーを書いている人と同じように。私が彼の会社がその時に利用可能にした唯一のコンパイラが「最適化できる頻繁に遭遇するステートメント」に基づいて最適化し、while(1)に遭遇したときに少しショートカットをしてすべてを評価したと思いますその他、while(2)を含む。そのような経験をしたことは彼の立場とそれに対する彼の自信を説明することができました。

あなたを雇うための最良のアプローチはおそらく、あなたが次の面接の主題にスムーズに導く前に、上級管理者が夢中になって「プログラミングの古き良き時代」について2〜3分間講義できるようにするアプローチでしょう。(ここでは適切なタイミングが重要です-速すぎてストーリーを中断している-遅すぎて、フォーカスが不十分な人物として分類されます)。面接の最後に、このトピックについて詳しく知りたいと彼に伝えてください。


19

あなたは彼にどのようにしてその結論に達したのか尋ねるべきだったのに。まともなコンパイラがあれば、2つは同じasm命令にコンパイルされます。だから、彼はコンパイラーに最初から始めるように言うべきでした。それでも、理論に基づいた推測を行うには、コンパイラとプラットフォームをよく理解している必要があります。そして、最終的には、メモリの断片化やシステムの負荷など、この詳細よりもループに影響を与える他の外部要因があるため、実際には問題になりません。


14
@GKFXあなたがあなたの答えを与えて、彼らがあなたが間違っているとあなたに言ったならば、あなたが彼らに理由を説明するように頼むことができない理由はありません。アナトリグが正しく、それがあなたの自信のテストである場合、あなたがあなたがした方法に答えた理由を説明し、彼らに同じように尋ねるべきです。
P.Turpie 2014

私はあなたが彼らに最初に言うことを意味しました。「なぜXが速いのか」にはなりません。「わかりません。なぜxが速いのですか?」当然のことながら、適切に回答した後は、質問することができます。
GKFX 2014

18

この質問のために、C委員会のDoug Gwynが、オプティマイザパスのない一部の初期のCコンパイラは、アセンブリのテストを生成するwhile(1)for(;;)それがない場合と比較して)と書いたことを覚えておきます。

私はこの歴史的メモを与えることによってインタビュアーに答え、それから私がコンパイラーがこれをしたことに非常に驚いたとしても、コンパイラーは次のことができると言います:

  • オプティマイザせずにコンパイラを渡すの両方のテストを生成while(1)し、while(2)
  • オプティマイザーパスwhile(1)を使用すると、慣用的と見なされるため、コンパイラーはすべて(無条件ジャンプで)最適化するように指示されます。これwhile(2)により、テストが終了するため、2つの間のパフォーマンスに違いが生じます。

私はもちろん考えていないことをインタビュアーに追加するwhile(1)while(2)、これらは同等の構成要素であるのと同じ構築物は、低品質の最適化の兆候です。


10

そのような質問に対する別の見方は、マネージャーに間違っていることをマネージャーに伝える勇気があるかどうかを確認することです。そして、どれだけ穏やかにそれを伝えることができます。

私の最初の本能は、適切なコンパイラーがそれを処理する必要があることをマネージャーに示すためにアセンブリー出力を生成することでした、そしてそれを行わない場合は、次のパッチを提出します:)


8

非常に多くの人々がこの問題を掘り下げているのを見るには、これが非常に迅速に物事を最適化したいかどうかを確認するためのテストである理由を正確に示します。

私の答えは、それはそれほど重要ではなく、私たちが解決しようとしているビジネス上の問題に焦点を当てています。結局のところ、それが私が支払われるものです。

さらに、私はwhile(1) {}それがより一般的であり、他のチームメートが誰かが1より大きい数を選ぶ理由を理解するために時間を費やす必要がないため、選択します。

コードを書いてみましょう。;-)


1
実行時間の需要に合わせるために1ミリ秒または2ミリ秒を削る必要があるいくつかのリアルタイムコードを最適化するために支払われている場合を除きます。もちろん、それはオプティマイザの仕事だと言う人もいます-それは、あなたのアーキテクチャにオプティマイザがある場合です。
Neowizard 14

6

最適化が心配な場合は、

for (;;)

テストがないからです。(シニックモード)


2
そのためwhile(2)、を使用するのが賢明です。最適化の余地for (;;)があり、パフォーマンスを向上させる準備ができたときに切り替えるだけです。
Groo

5

これは、技術的な質問としてマスクされた行動面接の質問の1つだと思います。一部の企業はこれを行います-彼らは有能なプログラマーが答えるのはかなり簡単であるはずの技術的な質問をしますが、インタビュイーが正しい答えを与えると、インタビュアーは彼らが間違っていると言います。

会社はあなたがこの状況でどう反応するかを見たがっています。あなたは静かにそこに座って、自信がないか面接官を混乱させる恐れがあるために、あなたの答えが正しいことを押し付けませんか?それとも、あなたが間違っていると知っている権威のある人に挑戦する用意がありますか?彼らはあなたが自分の信念に立ち向かう用意があるかどうか、そしてあなたがそれを巧妙で敬意を持って行うことができるかどうかを見たいのです。


3

私は、この種のナンセンスが違いを生んだかもしれないときに、Cとアセンブリのコードをプログラミングしていた。それが違いを生んだとき、私たちはそれを議会で書いた。

私がその質問をされた場合、私は時期尚早の最適化についてドナルドクヌースの有名な1974年の引用を繰り返し、インタビュアーが笑って先に進まなければ歩きました。


1
彼はまた、「確立されたエンジニアリング分野では、容易に得られる12%の改善は決して限界的とは見なされず、ソフトウェアエンジニアリングにも同じ視点が広がるはずだ」と述べたので、あなたが歩くことは不当だと思います。
アリス

3

インタビュアーが意図的にそのようなばかげた質問を投げかけ、あなたに3つのポイントを作ってほしかったのかもしれません。

  1. 基本的な推論。両方のループは無限であり、パフォーマンスについて話すのは難しいです。
  2. 最適化レベルに関する知識。特に、ブロックが空でない場合は、コンパイラーに最適化を行わせると条件が最適化されます。
  3. マイクロプロセッサのアーキテクチャに関する知識。ほとんどのアーキテクチャには、0と比較するための特別なCPU命令があります(必ずしも高速であるとは限りません)。

2

ここに問題があります。実際にプログラムを作成してその速度を測定すると、両方のループの速度が異なる可能性があります。いくつかの合理的な比較のために:

unsigned long i = 0;
while (1) { if (++i == 1000000000) break; }

unsigned long i = 0;
while (2) { if (++i == 1000000000) break; }

時間を出力するコードを追加すると、ループが1つまたは2つのキャッシュライン内に配置される方法などのランダムな効果によって違いが生じる可能性があります。たまたま、1つのループが完全に1つのキャッシュライン内にある場合や、キャッシュラインの先頭にある場合や、2つのキャッシュラインにまたがる場合があります。その結果、面接担当者が最速と主張するものは、偶然にも実際には最速になる可能性があります。

最悪の場合のシナリオ:最適化コンパイラーはループの機能を理解しませんが、2番目のループが実行されたときに生成される値は、最初のループによって生成される値と同じであることを理解します。そして、最初のループの完全なコードを生成しますが、2番目のループは生成しません。


1
「キャッシュライン」推論は、非常に小さなキャッシュを持つチップでのみ機能します。
シャープトゥース2014

10
これは要点を外しています。速度に関する質問は、「他のすべてが等しい」と仮定しています。
ジムバルター2014

6
@JimBalter:これはスピードの問題ではなく、インタビュアーとの議論についての問題でした。そして、実際のテストを行うことは、インタビュアーが「正しい」ことを証明する可能性があります-彼の議論とは何の関係もないが純粋に偶然である理由のため。もし彼が間違っていることを証明しようとしたら、それはあなたを恥ずかしい状況に陥らせるでしょう。
gnasher729 2014

1
@ gnasher729すべての論理的な議論はさておき、あなたが話している偶然の場合は、見る価値があります。しかし、そのようなケースが存在すると盲目的に信じることは、単純なだけでなく愚かでもあります。何が、どのように、なぜそれが起こるのかを説明しない限り、この答えは役に立ちません。
user568109

4
@ gnasher729「これは速度の問題ではありませんでした」-不正な修辞手法を採用する人々については議論しません。
ジムバルター2014

2

彼らはどちらも同じです-同じです。

仕様によると、0でないものはすべて真と見なされるため、最適化を行わなくても、優れたコンパイラはwhile(1)またはwhile(2)のコードを生成しません。コンパイラはの簡単なチェックを生成し!= 0ます。


@djechlin-どちらも1 CPUサイクルかかるため。
UncleKing 14

コンパイラ定数はそれをフォールドするため、実行時に評価されることすらありません。
PaulHK 2018

2

人々がこの非常に簡単な質問をテストし、証明し、答えるのに費やした時間と労力の量から判断すると、質問をすることで両方とも非常に遅くなったと言います。

そして、それにもっと時間を費やすために...

「while(2)」はばかげています。なぜなら、

「while(1)」および「while(true)」は、「ブレーク」が確実に発生する条件に基づいてループ内のある段階で呼び出されることを期待する無限ループを作成するために歴史的に使用されています。

"1"は常にtrueと評価されるために存在するため、 "while(2)"は "while(1 + 1 == 2)"と言うのと同じくらい愚かであり、これもtrueと評価されます。

そして、もしあなたが完全にばかげたものになりたいのなら、次のようにしてください:-

while (1 + 5 - 2 - (1 * 3) == 0.5 - 4 + ((9 * 2) / 4)) {
    if (succeed())
        break;
}

あなたのコーダーがコードの実行に影響を与えないタイプミスをしたと思いたいのですが、彼が意図的に「2」を奇妙な目的でのみ使用した場合、コード全体に奇妙なsh!読んで作業してください。


作成したロールバックは、非常に高い担当者の編集を元に戻します。これらの人々は通常ポリシーを非常によく知っており、彼らにあなたにそれらを教えるためにここにいます。私はあなたの投稿の最後の行があなたの投稿に何も役に立たないことを見つけます。私が明らかに欠けているジョークを受け取ったとしても、私はそれをおしゃべりすぎると考え、追加の段落の価値はありません。
Palec 14

@Palec:コメントをありがとう。同じ人物が私の投稿の多くを編集し、ほとんどの場合サインオフを削除するだけであることがわかりました。だから私は彼が評判の悪党であり、それゆえ彼の評判だと思った。オンラインフォーラムで言語が不足し、一般的な礼儀がなくなったため、執筆に署名できなくなったのでしょうか。多分私は少し古い学校ですが、こんにちはとさようならを省略するのは失礼なようです。私たちはアプリを使用していますが、それでも人間です。
ekerner 2014

1
上のスタック交換挨拶、キャッチフレーズ、感謝など歓迎されていませんヘルプセンターでも、特に予想される動作について、同じことが述べられていますStack Exchangeのどの部分も、Stack Overflowでさえ、フォーラムではありません。これらはQ&Aサイトです。編集は違いの1つです。また、コメントは、ディスカッション(Stack Overflow Chat)ではなく、投稿に対するフィードバックを提供するためだけに役立つ必要があります。また、Q&Aがフォーラムと異なる点は他にもたくさんあります。詳しくはヘルプセンターメタスタックオーバーフローをご覧ください。
Palec 2014

2000人を超える担当者(編集権限)を持っている場合、編集からそれ以上担当者を獲得できないことに注意してください。なぜあなたが同意しない編集があなたの投稿に適用されたのか疑問がある場合、または誰かの不正行為を見つけた場合は、メタスタックオーバーフローで質問してください。エディターが間違ったことをした場合、彼らはmodによって通知される(多分彼らは気付かなかった)か、何らかの形で罰せられる可能性があります(意図的に悪意のある動作である場合)。それ以外の場合は、編集/動作が正しい理由を説明するポインタが表示されます。不要なロールバックはリビジョン履歴を乱雑にし、ロールバック戦争にあなたを連れて行くことができます。編集が正しくないことが本当に確かな場合にのみロールバックします。
Palec、2014

最後に、署名、挨拶、…:これらの手続きを含めないのは失礼に思えるかもしれませんが、信号対雑音比が向上し、情報が失われることはありません。あなたはあなたが欲しいニックネームを選ぶことができます、それはあなたの署名です。ほとんどの場所にもあなたのアバターがあります。
Palec 14

1

それはコンパイラに依存します。

コードを最適化する場合、または特定の命令セットに対して同じ数の命令で1と2をtrueと評価する場合、実行速度は同じになります。

実際のケースでは常に同じように高速ですが、これを異なる方法で評価すると、特定のコンパイラと特定のシステムを想像することができます。

つまり、これは実際には言語(C)関連の質問ではありません。


0

この質問に答えようとする人は最速のループを望んでいるので、他の答えで述べたように、両方が同じアセンブリコードに等しくコンパイルされると答えました。それでも、「ループ展開」を使用してインタビュアーに提案できます。whileループの代わりにdo {} whileループ。

注意:ループが少なくとも1回は必ず実行されるようにする必要があります。

ループの内側に中断条件が必要です。

また、そのようなループについては、個人的にはdo {} while(42)の使用を好みます。なぜなら、0以外の任意の整数が機能するからです。


なぜ彼はdo {whileループを提案するのでしょうか?While(1)(またはwhile(2))は同じことを行います。
Catsunami

whileを実行すると、余分なジャンプが1つ削除されるため、パフォーマンスが向上します。もちろん、ループが少なくとも1回実行されることがわかっている場合
Antonin GAVREL

0

明らかな答えは次のとおりです。投稿されたとおり、両方のフラグメントは同じようにビジーな無限ループを実行し、プログラムを無限に遅くします。

Cキーワードをマクロとして再定義すると、技術的には未定義の動作になりますが、これは、どちらかのコードフラグメントを高速化するために私が考える唯一の方法です。2つのフラグメントの上にこの行を追加できます。

#define while(x) sleep(x);

実際、のwhile(1)2倍の速度(または半分の速度)になりwhile(2)ます。


@MaxB:確かに、マクロのトリックはさらに歪められる必要があります。C標準では保証されていませんが、新しいバージョンは動作するはずです。
chqrlie

-4

なぜwhile(2)それが遅くなるのかについて私が考えることができる唯一の理由は:

  1. コードはループを最適化して

    cmp eax, 2

  2. 減算が発生すると、基本的に減算しています

    a。 00000000 - 00000010 cmp eax, 2

    の代わりに

    b。 00000000 - 00000001 cmp eax, 1

cmpフラグを設定するだけで、結果は設定しません。したがって、最下位ビットでは、bを使用して借用する必要があるかどうかを確認します。一方、借りを取得する前に、2つの減算を実行する必要があります。


15
cmpは、どちらの場合でも、1 CPUサイクルかかります。
UncleKing 14

8
そのコードは正しくありません。正しいコードが0でEAXコンペアレジスタEAXに2または1をロードする
gnasher729
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.