Mana yang akan dieksekusi lebih cepat, if (flag == 0) atau if (0 == flag)?

111

Pertanyaan wawancara: Mana yang akan mengeksekusi lebih cepat, if (flag==0)atau if (0==flag)? Mengapa?

Vishwanath Dalvi
sumber
330
Dinominasikan untuk pertanyaan wawancara terbodoh yang pernah ada. Dan ada persaingan yang ketat.
Konrad Rudolph
119
Anda: Sebutkan situasi di mana perbedaan antara keduanya mungkin layak untuk diganggu. Pewawancara: Oke, Anda diterima.
Chris Lutz
37
Satu-satunya perbedaan antara keduanya adalah bahwa dengan konvensi selanjutnya, Anda diasuransikan terhadap bug seperti if(flag = 0)dengan harga sedikit keterbacaan.
Amarghosh
22
@Amarghosh: Dengan konsekuensi membuat kode Anda sulit dibaca dan tidak intuitif. Gunakan yang pertama saat menghidupkan peringatan kompiler Anda, win-win.
GManNickG
129
Suatu ketika seorang penulis kompilator mendapatkan ini dalam wawancaranya. Dia berbisik sebagai jawaban, "yang mana yang kamu inginkan lebih cepat?".

Jawaban:

236

Saya belum melihat jawaban yang benar (dan sudah ada beberapa) peringatan: Nawaz memang menunjukkan jebakan yang ditentukan pengguna . Dan saya menyesal karena terburu-buru memberikan suara positif pada "pertanyaan paling bodoh" karena tampaknya banyak yang tidak menjawabnya dengan benar dan ini memberi ruang untuk diskusi yang bagus tentang pengoptimalan kompiler :)

Jawabannya adalah:

Apa flagtipenya?

Dalam kasus di mana flagsebenarnya adalah tipe yang ditentukan pengguna. Kemudian tergantung pada overload mana yang operator==dipilih. Tentu saja terlihat bodoh bahwa mereka tidak akan simetris, tetapi itu pasti diperbolehkan, dan saya sudah melihat pelanggaran lain.

Jika flagbuilt-in, maka keduanya harus mengambil kecepatan yang sama.

Dari artikel Wikipedia pada x86, saya berani bertaruh untuk Jxxinstruksi untuk ifpernyataan: mungkin JNZ(Langsung jika Tidak Nol) atau setara.

Saya ragu kompilator melewatkan optimasi yang begitu jelas, bahkan dengan optimasi dimatikan. Ini adalah jenis hal yang dirancang untuk Pengoptimalan Lubang intip .

EDIT: Munculkan lagi, jadi mari tambahkan beberapa assembly (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
}

Bahkan jika seseorang tidak tahu cara membaca IR, saya pikir itu sudah cukup jelas.

Matthieu M.
sumber
4
@ Matthieu: Anda bilang saya belum melihat jawaban yang benar .. tapi jawaban saya benar, saya pikir: P
Nawaz
7
baik! jawaban Anda mungkin mengubah "pertanyaan terbodoh" menjadi "tipuan / paling kejam". "mari kita gali lubang untuk kandidat dan lihat apakah dia jatuh ke dalamnya ..." :) Saya kira kita semua otomatis berasumsi bahwa itu flagpasti bilangan bulat atau boolean. OTOH, memiliki variabel bernama flagdari tipe yang ditentukan pengguna cukup salah pada dirinya sendiri, IMHO
davka
@Nawaz: Saya mungkin telah melewatkan paragraf terakhir dari jawaban Anda: p
Matthieu M.
1
@Nawaz: Saya tidak terlalu berlomba, saya biasanya membaca pertanyaan lama setelah dijawab dan orang cenderung hanya membaca jawaban yang paling banyak dipilih :) Tapi saya sebenarnya membaca hal-hal tentang pengoptimalan compiler, dan ini menurut saya sebagai kasus tipikal pengoptimalan sepele jadi saya pikir saya akan menunjukkannya kepada para pembaca yang benar-benar peduli ... Saya cukup terkejut sebenarnya saya mendapat begitu banyak suara positif. Sekarang ini adalah jawaban saya yang paling banyak dipilih meskipun jelas bukan jawaban yang paling saya usahakan: / Bagaimanapun saya mengedit jawaban saya dan mengoreksi pernyataan saya :)
Matthieu M.
2
@mr_eclair: tipe bawaan adalah tipe yang (sesuai namanya) bawaan dalam bahasa. Artinya, ini tersedia bahkan tanpa satu #includepetunjuk pun . Untuk mudahnya, biasanya sebesar int, char, booldan sejenisnya. Semua jenis lain dikatakan user-defined, yaitu mereka ada karena mereka adalah hasil dari beberapa pengguna menyatakan mereka: typedef, enum, struct, class. Misalnya, std::stringadalah ditentukan pengguna, meskipun Anda pasti tidak mendefinisikannya sendiri :)
Matthieu M.
56

Kode yang sama untuk amd64 dengan GCC 4.1.2:

        .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
skc
sumber
18
1 untuk bekerja ekstra untuk membuktikan bahwa pengoptimalan compiler adalah sama.
k rey
56

Tidak akan ada perbedaan dalam versi Anda.

Saya berasumsi bahwa typeflag bukanlah tipe yang ditentukan pengguna, melainkan beberapa tipe bawaan. Enum adalah pengecualian! . Anda dapat memperlakukan enum seolah-olah itu ada di dalamnya. Faktanya, nilai itu adalah salah satu tipe bawaan!

Dalam kasus, jika itu tipe yang ditentukan pengguna (kecuali enum), maka jawabannya sepenuhnya tergantung pada bagaimana Anda membebani operator ==. Perhatikan bahwa Anda telah membebani ==dengan mendefinisikan dua fungsi, satu untuk setiap versi Anda!

Nawaz
sumber
8
ini bisa menjadi satu-satunya alasan yang mungkin untuk mengajukan pertanyaan ini, IMHO
davka
15
Saya akan sangat terkejut jika kompiler modern melewatkan pengoptimalan yang begitu jelas.
Pedro d'Aquino
3
Setahu saya ! bukan operasi yang
bijaksana
8
@Nawaz: tidak downvote tapi jawaban anda salah secara faktual dan sangat buruk karena masih mendapat banyak upvote. Sebagai catatan, membandingkan bilangan bulat dengan 0 adalah instruksi perakitan tunggal , sepenuhnya setara dengan negasi. Faktanya, jika kompilernya sedikit bodoh maka ini bahkan bisa lebih cepat daripada negasi (meskipun tidak mungkin).
Konrad Rudolph
6
@Nawaz: masih salah untuk mengatakan bahwa itu bisa, akan, atau biasanya akan, lebih cepat. Jika ada perbedaan, maka versi "bandingkan dengan nol" akan lebih cepat, karena negasi satu benar-benar diterjemahkan ke dalam dua operasi: "meniadakan operan; periksa hasilnya bukan nol". Dalam praktiknya, tentu saja, compiler mengoptimalkannya untuk menghasilkan kode yang sama dengan versi biasa "bandingkan dengan nol", tetapi pengoptimalan tersebut diterapkan ke versi negasi, untuk membuatnya menyusul, dan bukan sebaliknya. Konrad benar.
jalf
27

Sama sekali tidak ada perbedaan.

Anda mungkin mendapatkan poin dalam menjawab pertanyaan wawancara itu dengan merujuk pada penghapusan kesalahan ketik tugas / perbandingan, meskipun:

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

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

Meskipun benar, misalnya kompilator-C memang memperingatkan dalam kasus yang sebelumnya ( flag = 0), tidak ada peringatan seperti itu di PHP, Perl atau Javascript atau <insert language here>.

Linus Kleen
sumber
@Pengen Saya pasti melewatkan posting di meta yang menjelaskan gaya bracing yang "tepat".
Linus Kleen
7
Saya belum memberikan suara sama sekali, tetapi untuk apa nilainya: mengapa begitu penting bagi orang-orang untuk menjelaskan diri mereka sendiri setiap kali mereka memberikan suara? Suara secara anonim dirancang. Saya sepenuhnya menentang gagasan bahwa downvoters harus selalu berkomentar, karena saya pribadi tidak pernah ingin dianggap downvoter hanya karena saya meninggalkan komentar yang menunjukkan masalah. Mungkin downvoter mengira sebagian besar jawaban tidak relevan dengan pertanyaan kecepatan? Mungkin dia pikir itu mendorong gaya pengkodean yang tidak dia setujui? Mungkin dia brengsek, dan ingin jawabannya sendiri memiliki peringkat tertinggi?
David Hedlund
3
Orang harus bebas memilih apa pun yang mereka mau, apa pun alasannya. Dari segi reputasi, hal ini hampir selalu merupakan hal yang baik karena sering kali memicu orang lain untuk memberikan suara positif, untuk melawan suara negatif yang tidak layak, padahal, satu suara positif akan membatalkan lima suara negatif yang tidak layak.
David Hedlund
26
@David: Downvoters harus menjelaskan diri mereka sendiri karena situs ini bukan tentang surat suara popularitas rahasia, pemungutan suara anonim, atau sejenisnya. Situs ini tentang belajar. Jika seseorang mengatakan bahwa tanggapannya salah dengan downvoting itu, downvoter tersebut menjadi egois dengan pengetahuan mereka jika mereka tidak menjelaskan mengapa. Mereka bersedia mengambil semua pujian ketika mereka benar, tetapi tidak mau berbagi pengetahuan ketika orang lain salah.
John Dibling
1
Hanya untuk menghilangkan masalah gaya bracing, saya benar-benar berpikir Matthieu bermaksud itu sebagai lelucon. Saya akan terkejut melihat bahwa ada yang memberikan suara mereka tergantung pada masalah seperti itu. Karena itu, tidak semua orang menggunakan suara dengan cara yang persis sama. Saya dapat melihat alasan untuk downvoting karena postingan tersebut tampaknya menganjurkan gaya pengkodean yang mungkin tidak disetujui oleh pemilih (perhatikan perbedaan antara menganjurkan gaya pengkodean - "jika Anda menulis kode seperti ini, Anda akan mendapatkan kesalahan penyusun saat membuat kesalahan ketik ini "- dan hanya menggunakan gaya pengkodean, seperti tanda kurung) Dalam ...
David Hedlund
16

Sama sekali tidak akan ada perbedaan dalam hal kecepatan. Kenapa harus ada?

Jon
sumber
7
jika kompilator benar-benar terbelakang. Itulah satu-satunya alasan.
JeremyP
@ JeremyP: Saya tidak bisa membayangkan perbedaan bahkan jika kompilernya terbelakang. Sejauh yang saya tahu, penulis kompilator harus melakukannya dengan sengaja .
Jon
2
Dengan asumsi prosesor memiliki instruksi "uji jika 0", x == 0mungkin menggunakannya tetapi 0 == xmungkin menggunakan perbandingan normal. Saya memang mengatakan itu harus terbelakang.
JeremyP
8
Jika flag adalah tipe yang ditentukan pengguna dengan overload asimetris operator == ()
OrangeDog
Karena kita mungkin memiliki virtual operator==(int)tipe yang ditentukan pengguna?
lorro
12

Ada perbedaan ketika flag adalah tipe yang ditentukan pengguna

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;
    }
}

Dalam kasus pertama (0 == s) operator konversi dipanggil dan kemudian hasil yang dikembalikan dibandingkan dengan 0. Dalam kasus kedua operator == dipanggil.

ds27680
sumber
3
+1 untuk menyebutkan bahwa operator konversi bisa serelevan dengan operator ==.
Tony Delroy
11

Jika ragu, tandai dan pelajari kebenaran.

Elzo Valugi
sumber
2
ada apa dengan benchmarking? terkadang praktik ini memberi tahu Anda lebih dari sekadar teori
Elzo Valugi
1
Itulah jawaban yang saya cari ketika saya mulai membaca utas ini. Tampaknya teori itu lebih menarik daripada praktik, mencari jawaban dan suara positif :)
Samuel Rivas
bagaimana dia bisa menjadi patokan saat wawancara? ditambah lagi saya pikir pewawancara bahkan tidak tahu apa artinya benchmarking, jadi dia bisa saja tersinggung.
IAdapter
Mereka jawaban yang tepat untuk pertanyaan (IMO) adalah "Itu sangat tergantung pada compiler dan program lainnya. Saya akan menulis benchmark dan mengujinya dalam 5 menit"
Samuel Rivas
7

Mereka harus persis sama dalam hal kecepatan.

Namun perhatikan bahwa beberapa orang menggunakan konstanta di sebelah kiri dalam perbandingan persamaan (yang disebut "Yoda conditional") untuk menghindari semua kesalahan yang mungkin muncul jika Anda menulis =(operator penugasan) dan bukan ==(operator perbandingan persamaan); karena menugaskan ke literal memicu kesalahan kompilasi, kesalahan semacam ini dihindari.

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
{

}

Di sisi lain, kebanyakan orang menganggap "Yoda conditionals" tampak aneh dan menjengkelkan, terutama karena kelas kesalahan yang mereka cegah dapat dilihat juga dengan menggunakan peringatan kompiler yang memadai.

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

}
Matteo Italia
sumber
Terima kasih telah menggema. Perhatikan, bahwa PHP misalnya tidak akan memberi peringatan jika ada penugasan dalam kondisi bersyarat.
Linus Kleen
5

Seperti yang dikatakan orang lain, tidak ada perbedaan.

0harus dievaluasi. flagharus dievaluasi. Proses ini membutuhkan waktu yang sama, tidak peduli di sisi mana mereka ditempatkan.

Jawaban yang tepat adalah: keduanya memiliki kecepatan yang sama.

Bahkan ekspresi if(flag==0)dan if(0==flag)jumlah karakternya sama! Jika salah satunya ditulis sebagai if(flag== 0), maka kompilator akan memiliki satu ruang ekstra untuk diurai, jadi Anda akan memiliki alasan yang sah untuk menunjukkan waktu kompilasi.

Tetapi karena tidak ada hal seperti itu, sama sekali tidak ada alasan mengapa seseorang harus lebih cepat dari yang lain. Jika ada alasannya, maka kompilator melakukan beberapa hal yang sangat, sangat aneh untuk membuat kode ...

darioo
sumber
5

Puasa mana yang tergantung pada versi == yang Anda gunakan. Berikut cuplikan yang menggunakan 2 kemungkinan penerapan ==, dan bergantung pada apakah Anda memilih untuk memanggil x == 0 atau 0 == x salah satu dari 2 dipilih.

Jika Anda hanya menggunakan POD, ini seharusnya tidak menjadi masalah dalam hal kecepatan.

#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);
}
Fanatik 23
sumber
5

Baiklah, saya setuju sepenuhnya dengan semua yang dikatakan di komentar kepada OP, demi latihan:

Jika kompilator tidak cukup pintar (memang Anda tidak boleh menggunakannya) atau pengoptimalan dinonaktifkan, x == 0dapat dikompilasi ke jump if zeroinstruksi perakitan asli , sementara itu 0 == xbisa menjadi perbandingan nilai numerik yang lebih umum (dan mahal).

Tetap saja, saya tidak ingin bekerja untuk bos yang berpikir seperti ini ...

davka
sumber
4

Pastinya tidak ada perbedaan dalam hal kecepatan eksekusi. Kondisi tersebut perlu dievaluasi pada kedua kasus dengan cara yang sama.

Sachin Shanbhag
sumber
3

Saya pikir jawaban terbaik adalah "dalam bahasa apa contoh ini"?

Pertanyaan tidak menentukan bahasanya dan diberi tag 'C' dan 'C ++'. Jawaban yang tepat membutuhkan lebih banyak informasi.

Ini adalah pertanyaan pemrograman yang buruk, tetapi bisa juga bagus dalam departemen "mari beri orang yang diwawancarai cukup tali untuk menggantung diri atau membangun ayunan pohon" yang licik. Masalah dengan pertanyaan semacam itu adalah biasanya mereka ditulis dan diturunkan dari pewawancara ke pewawancara hingga sampai ke orang-orang yang tidak benar-benar memahaminya dari semua sudut.

Marsh Ray
sumber
3

Buat dua program sederhana menggunakan cara yang disarankan.

Kumpulkan kodenya. Lihatlah majelis dan Anda bisa menilai, tapi saya ragu ada perbedaannya!

Wawancara semakin rendah dari sebelumnya.

Kesalahan sintaks
sumber
2

Sekadar tambahan (menurut saya kompiler yang layak akan membuat pertanyaan ini diperdebatkan, karena ini akan mengoptimalkannya) menggunakan 0 == flag over flag == 0 memang mencegah kesalahan ketik di mana Anda lupa salah satu dari = (yaitu jika Anda tidak sengaja mengetik flag = 0 itu akan dikompilasi, tetapi 0 = flag tidak akan), yang menurut saya adalah kesalahan yang dilakukan semua orang pada satu titik atau lainnya ...

Kindread
sumber
0

Jika memang ada perbedaan, apa yang menghentikan compiler untuk memilih yang lebih cepat sekali? Jadi secara logika, tidak mungkin ada perbedaan apapun. Mungkin inilah yang diharapkan pewawancara. Ini sebenarnya pertanyaan yang brilian.

balki
sumber