(flag == 0)または(0 == flag)の場合、どちらがより速く実行されますか?


111

インタビューの質問:どちらが速く実行されますif (flag==0)if (0==flag)?どうして?


330
これまでで最も愚かなインタビューの質問にノミネートされました。そして、厳しい競争があります。
Konrad Rudolph

119
あなた:これら2つの違いが気になる可能性のある状況を挙げてください。インタビュアー:さて、あなたは雇われました。
Chris Lutz

37
2つの唯一の違いは、後者の規則でif(flag = 0)は、可読性が少し犠牲になるなどのバグに対して保証されることです。
Amarghosh、2011年

22
@Amarghosh:コードを読みにくくし、直感的ではない犠牲を払ってください。前者のコンパイラ警告を順番に使用してください、win-win。
GManNickG 2011年

129
コンパイラの作者が彼のインタビューでこれを得たら。彼は、応答でささやいた「一つはやるどのあなたが速くなりたいです?」。

回答:


236

正解はまだ見ていません(すでにいくつかあります)警告:Nawazはユーザー定義のトラップを指摘しました。そして、私が急いで「愚かな質問」に賛成票を投じたことを後悔しているのは、多くの人が正しく理解していないようで、コンパイラーの最適化に関する素晴らしい議論の余地があるからです:)

答えは:

flagのタイプは何ですか?

場合にflag、実際にユーザ定義型です。次に、どのオーバーロードoperator==が選択されているかによって異なります。もちろん、それらが対称的ではないのは愚かに見えるかもしれませんが、それは確かに許可されており、私はすでに他の虐待を見てきました。

もし flagが組み込みの、どちらも同じ速度になります。

Wikipediaの記事x86、私はのために賭ける思いJxxのための指示if書:おそらくJNZ(ジャンプゼロでない場合)またはいくつかの同等のものを。

最適化がオフになっている場合でも、コンパイラーがこのような明らかな最適化を見逃しているとは思いません。これは、Peephole Optimizationが設計されているタイプの物です。

編集:再び湧き上がったので、いくつかのアセンブリを追加しましょう(LLVM 2.7 IR)

int regular(int c) {
  if (c == 0) { return 0; }
  return 1;
}

int yoda(int c) {
  if (0 == c) { return 0; }
  return 1;
}

define i32 @regular(i32 %c) nounwind readnone {
entry:
  %not. = icmp ne i32 %c, 0                       ; <i1> [#uses=1]
  %.0 = zext i1 %not. to i32                      ; <i32> [#uses=1]
  ret i32 %.0
}

define i32 @yoda(i32 %c) nounwind readnone {
entry:
  %not. = icmp ne i32 %c, 0                       ; <i1> [#uses=1]
  %.0 = zext i1 %not. to i32                      ; <i32> [#uses=1]
  ret i32 %.0
}

IRの読み方がわからなくても自明だと思います。


4
@Matthieu:正解はまだ見ていなかったとおっしゃいましたが、私は正しいと思います:P
Nawaz

7
良い!可能な答えは「愚かな質問」を「トリッキー/真面目」に変えます。「候補者のための穴を掘って、彼がそれに陥るかどうか見てみましょう...」:)私たちは皆、それflagが整数またはブール値でなければならないことを自動的に想定していると思います。OTOH、flagユーザー定義型の名前が付けられた変数を持つこと自体、
IMHO

@Nawaz:私はあなたの回答の最後の段落をスキップしたかもしれません:p
Matthieu M.

1
@Nawaz:私は実際には競争しません、私は通常、質問が回答されてからずっと後に読んで、人々は最初に最も投票された回答のみを読む傾向があります:)しかし、私は実際にコンパイラの最適化に関するものを読んでいるので、ささいな最適化の典型的なケースなので、実際に気になる読者のためにそれを指摘したいと思いました...実際に非常に多くの賛成票を獲得したことに私はかなり驚いています。確かに私が最も努力したものではありませんが、それが私の最も投票された回答です:/とにかく私は私の回答を編集して私のステートメントを修正しました:)
Matthieu M.

2
@mr_eclair:組み込み型は、その言語で(名前が示すとおり)組み込み型です。つまり、単一の#includeディレクティブがなくても使用できます。簡単にするために、通常はintcharboolなどになります。他のすべての種類は、彼らがそれらを宣言する一部のユーザーの結果であるので、彼らは存在していること、ユーザ定義であると言われています:typedefenumstructclass。たとえば、std::string自分で定義しなかったとしても、ユーザー定義です:)
Matthieu M.

56

GCC 4.1.2でのamd64の同じコード:

        .loc 1 4 0  # int f = argc;
        movl    -20(%rbp), %eax
        movl    %eax, -4(%rbp)
        .loc 1 6 0 # if( f == 0 ) {
        cmpl    $0, -4(%rbp)
        jne     .L2
        .loc 1 7 0 # return 0;
        movl    $0, -36(%rbp)
        jmp     .L4
        .loc 1 8 0 # }
 .L2:
        .loc 1 10 0 # if( 0 == f ) {
        cmpl    $0, -4(%rbp)
        jne     .L5
        .loc 1 11 0 # return 1;
        movl    $1, -36(%rbp)
        jmp     .L4
        .loc 1 12 0 # }
 .L5:
        .loc 1 14 0 # return 2;
        movl    $2, -36(%rbp)
 .L4:
        movl    -36(%rbp), %eax
        .loc 1 15 0 # }
        leave
        ret

18
+1は、コンパイラの最適化が同じであることを証明するためにさらに1マイル進みます。
k rey

56

バージョンに違いはありません。

typeofフラグはユーザー定義型ではなく、組み込み型であると想定しています。Enumは例外です!。enumを組み込みのように扱うことができます。実際、その値は組み込み型の1つです。

その場合、それがユーザー定義型(を除くenum)の場合、答えは完全に演算子をどのようにオーバーロードしたかに依存します====バージョンごとに1つずつ、2つの関数を定義してオーバーロードする必要があることに注意してください。


8
これがこの質問をする唯一の考えられる理由かもしれません、私見
davka

15
現代のコンパイラがそのような明らかな最適化を見逃したとしたら、私は非常に驚きます。
ペドロ・ドール・アキノ

3
私の知る限り !はビット単位の演算ではありません
Xavier Combelle 2011年

8
@Nawaz:反対投票はしませんでしたが、あなたの答えは事実上間違っており、それでもそれでも多くの反対投票があったのは恐ろしいことです。記録では、整数を0と比較することは、完全に否定と同等の単一のアセンブリ命令です。実際、コンパイラーが少し愚かであれば、これは否定より速いかもしれません(そうは思われません)。
Konrad Rudolph

6
@Nawaz:それが可能であるか、そうであるか、または通常はより速くなると言うことはまだ間違っています。違いがある場合は、「ゼロと比較」バージョンの方が高速になります。これは、否定は実際には2つの演算に変換されるためです:「オペランドを否定する;結果がゼロでないことを確認する」。もちろん実際には、コンパイラーはそれを最適化してプレーンな「ゼロで比較」バージョンと同じコードを生成しますが、最適化は否定バージョンに適用され、追いつくようにします。コンラートは正しい。
2001年

27

全く違いはありません。

ただし、割り当て/比較のタイプミスの排除を参照することで、そのインタビューの質問に答えることでポイントを獲得できます。

if (flag = 0)  // typo here
   {
   // code never executes
   }

if (0 = flag) // typo and syntactic error -> compiler complains
   {
   // ...
   }

それは事実ですが、たとえば前者(flag = 0)の場合はCコンパイラが警告を発しますが、PHP、Perl、Javascriptまたはにはそのような警告はありません<insert language here>


@Matthieu Huh。「適切な」ブレーススタイルを説明するメタに関する投稿を見逃していたに違いありません。
Linus Kleen、2011年

7
私はまったく投票していませんが、それだけの価値があるのはなぜですか。投票するたびに、人々が自分を説明することがそれほど重要なのはなぜですか。投票は設計により匿名です。問題を指摘するコメントを残したからといって、個人的にダウンボーターであると思われたくないので、ダウンボーターは常にコメントするべきだという考えにはまったく反対です。おそらく、反対投票者は答えの大部分は速度の問題とは無関係であると考えましたか?おそらく彼はそれが彼が承認しなかったコーディングスタイルを奨励したと思ったのですか?多分彼はディックであり、彼自身の答えが最高の評価を得ることを望んでいましたか?
David Hedlund、2011年

3
理由に関係なく、人々は自由に投票できるべきです。評判的には、これはほとんどの場合良いことです。実際には、1つの賛成投票で5つの不適切な反対投票がキャンセルされ、反対の投票に反対するために、他の人が反対票を投じることがよくあるからです。
David Hedlund

26
@David:このサイトは秘密の人気投票、匿名投票などに関するものではないため、ダウン投票者は自分で説明する必要があります。このサイトは学習についてです。誰かが反対票を投じることによって応答が正しくないと言った場合、反対投票者は、理由を説明しなければ、知識に利己的です。彼らは彼らが正しいときにすべてのクレジットを喜んで受け入れますが、他の人が間違っているときに知識を共有することを望んでいません。
John Dibling、2011年

1
筋かいスタイルの問題を解決するために、私はマティウが冗談として意図したと本当に思っています。そのような問題に応じて、誰もが自分の票を投じるのを見て、私は驚きます。とはいえ、投票がまったく同じ方法で行われるわけではありません。ポストは、有権者がで非難かもしれないというコーディングスタイルを提唱するようなので、私はdownvotingのための根拠を見ることができました(との違いに注意提唱コーディングスタイルを- 「あなたは、このようなコードを書いた場合、あなたが作るときに、コンパイラエラーが発生しますthis typo "-そして、ブレースなどのコーディングスタイルを使用するだけです)その中で...
David Hedlund

16

速度的にはまったく違いはありません。なぜあるべきなのか?


7
コンパイラが完全に遅延した場合。それが唯一の理由です。
JeremyP 2011年

@JeremyP:コンパイラが遅れていても違いは想像できません。コンパイラの作者は、私が知る限り、わざとそうする必要があります。
Jon

2
プロセッサに "test if 0"命令があると仮定すると、x == 0それを0 == x使用できますが、通常の比較を使用できます。遅らせる必要があると私は言った。
JeremyP、2011年

8
flagが、operator ==()の非対称オーバーロードを持つユーザー定義型の場合
OrangeDog

我々はそのためかもしれない持っているvirtual operator==(int)ユーザー定義型では?
lorro

12

フラグがユーザー定義型の場合は違いがあります

struct sInt
{
    sInt( int i ) : wrappedInt(i)
    {
        std::cout << "ctor called" << std::endl;
    }

    operator int()
    {
        std::cout << "operator int()" << std::endl;
        return wrappedInt;
    }

    bool operator==(int nComp)
    {
        std::cout << "bool operator==(int nComp)" << std::endl;
        return (nComp == wrappedInt);
    }

    int wrappedInt;
};

int 
_tmain(int argc, _TCHAR* argv[])
{
    sInt s(0);

    //in this case this will probably be faster
    if ( 0 == s )
    {
        std::cout << "equal" << std::endl;
    }

    if ( s == 0 )
    {
        std::cout << "equal" << std::endl;
    }
}

最初のケース(0 == s)では、変換演算子が呼び出され、返された結果が0と比較されます。2番目のケースでは、==演算子が呼び出されます。


3
+1は、変換演算子がoperator ==と同じくらい関連性がある可能性があることを言及します。
Tony Delroy、2011

11

疑わしいときはベンチマークして真実を学んでください。


2
ベンチマークの何が問題になっていますか?時々練習は理論より多くをあなたに話している
Elzo Valugi

1
それが、このスレッドを読み始めたときに私が探していた答えです。理論は実践よりも魅力的で、回答と賛成投票を探しているようです:)
Samuel Rivas

どのように彼はインタビューでベンチマークをとることができますか?加えて、面接担当者はベンチマークの意味すら知らないので、彼は気分を害したかもしれません。
IAdapter 2011年

質問(IMO)に対する正しい答えは、「コンパイラーとプログラムの残りの部分に大きく依存します。ベンチマークを記述して、5分でテストします」
Samuel Rivas

7

速度の点でまったく同じでなければなりません。

ただし===(等価演算子)ではなく(代入演算子)と書くと発生する可能性のあるすべてのエラーを回避するために、等値比較(いわゆる「ヨーダ条件」)で定数を左側に置く人がいることに注意してください。リテラルに割り当てるとコンパイルエラーが発生するため、この種のミスは回避されます。

if(flag=0) // <--- typo: = instead of ==; flag is now set to 0
{
    // this is never executed
}

if(0=flag) // <--- compiler error, cannot assign value to literal
{

}

一方、ほとんどの人は「ヨーダの条件文」を奇妙に見させてイライラさせます。特に、適切なコンパイラ警告を使用することで、防止するエラーのクラスを見つけることができるためです。

if(flag=0) // <--- warning: assignment in conditional expression
{

}

エコーしてくれてありがとう。ただし、たとえばPHPは条件付きの代入の場合には警告を発しないことに注意してください。
Linus Kleen、2011年

5

他の人が言ったように、違いはありません。

0評価する必要があります。flag評価する必要があります。どちらの側に配置しても、このプロセスには同じ時間がかかります。

正解は次のとおりです。どちらも同じ速度です。

でも表現if(flag==0)if(0==flag)文字の同じ量を持っています!それらの1つがとして記述されているif(flag== 0)場合、コンパイラーは解析する1つの余分なスペースを持つため、コンパイル時間を指摘することには正当な理由があります。

しかし、そのようなことはないので、他の人よりも速くあるべき理由は絶対にありません。理由がある場合、コンパイラは生成されたコードに対して非常に奇妙なことをしています...


5

どちらが速いかは、使用している==のバージョンによって異なります。==の2つの可能な実装を使用するスニペットは次のとおりです。x== 0または0 == xのどちらを呼び出すかによって、2つのうちの1つが選択されます。

PODだけを使用している場合は、速度に関しては問題になりません。

#include <iostream>
using namespace std;

class x { 
  public:
  bool operator==(int x) { cout << "hello\n"; return 0; }
  friend bool operator==(int x, const x& a) { cout << "world\n"; return 0; } 
};

int main()
{ 
   x x1;
   //int m = 0;
   int k = (x1 == 0);
   int j = (0 == x1);
}

5

まあ、私は演習のために、OPへのコメントで述べられているすべてに完全に同意します。

コンパイラーが十分に賢くない(実際には使用しないでください)か、最適化が無効になっx == 0ている場合、ネイティブのアセンブリーjump if zero命令にコンパイルできますが、0 == x数値のより一般的な(そしてコストのかかる)比較になる可能性があります。

それでも、私はこれらの言葉で考える上司のために働きたくありません...



3

最良の答えは「この例の言語は何ですか」だと思いますか?

質問には言語が指定されておらず、「C」と「C ++」の両方のタグが付けられています。正確な答えには、より多くの情報が必要です。

これはお粗末なプログラミングの質問ですが、「面接対象者に首を吊るすか、または木のブランコを構築するのに十分なロープを与えましょう」という不正行為ではよいかもしれません。これらの種類の質問の問題は、通常、すべての角度から理解できない人に伝わるまで、インタビュアーからインタビュアーに書き留められることです。


3

提案された方法を使用して、2つの単純なプログラムを作成します。

コードを組み立てます。アセンブリを見て、あなたは判断できますが、違いがあるとは思えません!

インタビューはかつてないほど低くなっています。


2

余談ですが(実際には、適切なコンパイラーはこの質問を最適化するので、これは問題にならないと思います)0 == flag over flag == 0を使用すると、=の1つを忘れた場合のタイプミスが防止されます(つまり、誤って入力した場合)フラグ= 0はコンパイルされますが、0 =フラグはされません)。これは、誰もがどこかで行った間違いだと思います...


0

まったく違いがあった場合、コンパイラが一度高速を選択するのを止めるのは何ですか?したがって、論理的には違いはありません。おそらくこれはインタビュアーが期待していることです。それは実際には素晴らしい質問です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.