Mana yang lebih cepat: while (1) atau while (2)?

587

Ini adalah pertanyaan wawancara yang diajukan oleh manajer senior.

Mana yang lebih cepat?

while(1) {
    // Some code
}

atau

while(2) {
    //Some code
}

Saya mengatakan bahwa keduanya memiliki kecepatan eksekusi yang sama, karena ekspresi di dalam whileakhirnya harus dievaluasi trueatau false. Dalam hal ini, keduanya mengevaluasi truedan tidak ada instruksi bersyarat tambahan di dalam whilekondisi. Jadi, keduanya akan memiliki kecepatan eksekusi yang sama dan saya lebih suka saat (1).

Tetapi pewawancara berkata dengan percaya diri: "Periksa dasar-dasar Anda. Lebih while(1)cepat daripada while(2)." (Dia tidak menguji kepercayaan diri saya)

Apakah ini benar?

Lihat juga: Apakah "untuk (;;)" lebih cepat dari "sementara (BENAR)"? Jika tidak, mengapa orang menggunakannya?

Nikole
sumber
202
Kompiler yang setengah layak akan mengoptimalkan kedua bentuk tersebut menjadi nol.
64
Dalam build yang dioptimalkan setiap saat (n), n! = 0 atau untuk (;;) akan diterjemahkan ke Majelis tak berujung loop dengan label di awal dan kebagian di akhir. Kode yang persis sama, kinerja yang sama.
Alex F
60
Tidak mengherankan, pengoptimalan stok membawa 0x100000f90: jmp 0x100000f90(alamat bervariasi, jelas) untuk kedua snippet. Pewawancara mungkin melakukan lindung nilai pada tes register vs lompatan sederhana yang ditandai. Kedua pertanyaan, dan anggapan mereka, lemah.
WhozCraig
50
Pertanyaan ini oleh pewawancara berada di bawah naungan yang sama seperti dilbert.com/strips/comic/1995-11-17 - Anda akan bertemu seseorang yang benar-benar percaya dengan apa yang mereka katakan terlepas dari hasil kebodohan dalam pernyataan mereka. Cukup pilih dari yang berikut ini: bernafas dalam, bersumpah, tertawa, menangis, beberapa kombinasi dari yang di atas :)
GMasucci
3
@ Mike W: orang dapat bertanya-tanya apa yang harus dilakukan oleh kompiler: menerjemahkan ke pernyataan Berhenti, atau menganggap bahwa loop keluar setelah waktu tak terbatas dan mengoptimalkan jauh penundaan tak terbatas?
Yves Daoust

Jawaban:

681

Kedua loop tidak terbatas, tetapi kita dapat melihat mana yang membutuhkan lebih banyak instruksi / sumber daya per iterasi.

Dengan menggunakan gcc, saya mengkompilasi dua program berikut untuk perakitan pada berbagai tingkat optimasi:

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


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

Bahkan tanpa optimasi ( -O0), perakitan yang dihasilkan identik untuk kedua program . Oleh karena itu, tidak ada perbedaan kecepatan antara kedua loop.

Untuk referensi, berikut adalah rakitan yang dihasilkan (menggunakan gcc main.c -S -masm=inteldengan bendera optimisasi):

Dengan -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"

Dengan -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"

Dengan -O2dan -O3(keluaran yang sama):

    .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"

Bahkan, rakitan yang dihasilkan untuk loop identik untuk setiap tingkat optimasi:

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

Bit-bit penting adalah:

.L2:
    jmp .L2

Saya tidak bisa membaca perakitan dengan baik, tetapi ini jelas merupakan loop tanpa syarat. The jmpinstruksi tanpa syarat me-reset program kembali ke .L2label tanpa membandingkan nilai terhadap yang benar, dan tentu saja segera melakukannya lagi sampai program ini entah bagaimana berakhir. Ini berhubungan langsung dengan kode C / C ++:

L2:
    goto L2;

Edit:

Cukup menarik, bahkan tanpa optimasi , loop berikut semuanya menghasilkan output yang sama persis (tanpa syarat jmp) dalam perakitan:

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) {}

Dan bahkan keheranan saya:

#include<math.h>

while(sqrt(7)) {}

while(hypot(3,4)) {}

Hal-hal menjadi sedikit lebih menarik dengan fungsi yang ditentukan pengguna:

int x(void) {
    return 1;
}

while(x()) {}


#include<math.h>

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

while(x()) {}

Pada -O0, dua contoh ini benar-benar memanggil xdan melakukan perbandingan untuk setiap iterasi.

Contoh pertama (pengembalian 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"

Contoh kedua (kembali 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"

Namun, pada -O1dan di atas, mereka berdua menghasilkan rakitan yang sama seperti contoh sebelumnya ( jmpkembali tanpa syarat ke label sebelumnya).

TL; DR

Di bawah GCC, loop yang berbeda dikompilasi ke rakitan yang sama. Kompiler mengevaluasi nilai-nilai konstan dan tidak repot melakukan perbandingan yang sebenarnya.

Moral dari cerita ini adalah:

  • Ada lapisan terjemahan antara kode sumber C ++ dan instruksi CPU, dan lapisan ini memiliki implikasi penting untuk kinerja.
  • Oleh karena itu, kinerja tidak dapat dievaluasi hanya dengan melihat kode sumber.
  • Kompiler harus cukup pintar untuk mengoptimalkan kasus sepele seperti itu. Pemrogram tidak boleh membuang waktu untuk memikirkan mereka dalam sebagian besar kasus.
ApproachingDarknessFish
sumber
196
Mungkin pewawancara tidak menggunakan gcc
MM
106
@ Matt McNabb Itu poin yang baik, tetapi jika pewawancara mengandalkan optimisasi spesifik-kompiler, maka mereka harus sangat eksplisit tentang hal itu dalam pertanyaan mereka, dan mereka perlu menerima jawaban "tidak ada perbedaan" sebagai yang benar untuk beberapa kompiler (paling?).
Jonathan Hartley
109
Demi menghilangkan keraguan, saya menguji ini di dentang 3.4.2, dan kedua loop menghasilkan rakitan yang sama di setiap -Olevel.
Martin Tournoij
16
Saya tidak menemukan ini sama sekali mengejutkan karena semua yang Anda tempatkan di bagian kondisional dari loop adalah konstanta waktu kompilasi. Karena itu, saya akan curiga seorang kompiler akan melihat bahwa loopnya akan selalu benar atau salah dan dengan hormat hanya jmpkembali ke awal atau menghapus loop sepenuhnya.
sherrellbc
10
@hippietrail Saya tidak melihat bagaimana konten (atau ketiadaan) dari loop ini dapat mempengaruhi pengoptimalan ini (kecuali kemungkinan breakpernyataan), tapi saya tetap mengujinya dan tidak, bahkan dengan kode di dalam loop, lompatannya adalah mutlak dan tanpa syarat untuk keduanya while(1)dan while(2). Jangan ragu untuk menguji yang lain sendiri jika Anda benar-benar khawatir.
ApproachingDarknessFish
282

Ya, while(1)jauh lebih cepat daripada while(2), bagi manusia untuk membaca! Jika saya melihat while(1)dalam basis kode yang tidak dikenal, saya segera tahu apa yang dimaksudkan oleh penulis, dan bola mata saya dapat melanjutkan ke baris berikutnya.

Jika saya melihat while(2), saya mungkin akan berhenti di jalur saya dan mencoba mencari tahu mengapa penulis tidak menulis while(1). Apakah jari penulis tergelincir pada keyboard? Apakah pengelola basis kode ini menggunakan while(n)sebagai mekanisme komentar yang tidak jelas untuk membuat loop terlihat berbeda? Apakah ini solusi kasar untuk peringatan palsu di beberapa alat analisis statis yang rusak? Atau apakah ini petunjuk bahwa saya membaca kode yang dihasilkan? Apakah itu bug yang dihasilkan dari penemuan-dan-ganti-semua yang keliru, atau gabungan yang buruk, atau sinar kosmik? Mungkin baris kode ini seharusnya melakukan sesuatu yang sangat berbeda. Mungkin seharusnya membaca while(w)atau while(x2). Saya lebih baik menemukan penulis dalam sejarah file dan mengirimi mereka email "WTF" ... dan sekarang saya telah merusak konteks mental saya.while(2)while(1) akan mengambil sepersekian detik!

Saya melebih-lebihkan, tetapi hanya sedikit. Keterbacaan kode sangat penting. Dan itu layak disebut dalam sebuah wawancara!

Chris Culter
sumber
Pikiranku persis, dan tepatnya mengapa sementara (1) lebih cepat lol Meskipun saya pikir itu hanya kasus lama seorang manajer yang mencoba meyakinkan dirinya sendiri bahwa dia tahu tentang pengkodean ketika dia tidak, kita semua sudah melihatnya, semua orang ingin menjadi jedi.
ekerner
8
Tentu saja, ini TIDAK berlebihan. Jelas svn-annotate atau git blame(atau apa pun) akan digunakan di sini, dan secara umum dibutuhkan beberapa menit untuk memuat riwayat menyalahkan file. Kemudian akhirnya memutuskan "ah saya mengerti, penulis menulis baris ini ketika baru keluar dari sekolah", baru saja kehilangan 10 menit ...
v.oddou
11
Terpilih karena saya muak dengan konvensi. Pengembang memiliki kesempatan kecil ini untuk menulis nomor yang disukainya, lalu seseorang yang membosankan datang dan mengomel mengapa tidak 1.
Utkan Gezer
1
Turun karena retorika. Membaca saat (1) persis kecepatan yang sama seperti saat (2).
hdante
4
Saya telah melalui proses mental yang sama persis seperti yang dijelaskan dalam jawaban ini satu menit yang lalu ketika saya melihat while(2)dalam kode (turun untuk mempertimbangkan "Apakah pemelihara basis kode ini menggunakan sementara (n) sebagai mekanisme komentar yang tidak jelas untuk membuat loop terlihat berbeda?" ). Jadi tidak, Anda tidak melebih-lebihkan!
Hisyam HM
151

Jawaban yang ada menunjukkan kode yang dihasilkan oleh kompiler tertentu untuk target tertentu dengan serangkaian opsi tertentu tidak sepenuhnya menjawab pertanyaan - kecuali pertanyaan itu diajukan dalam konteks tertentu ("Yang lebih cepat menggunakan gcc 4.7.2 untuk x86_64 dengan opsi default? ", misalnya).

Sejauh definisi bahasa yang bersangkutan, dalam mesin abstrak while (1) mengevaluasi konstanta integer 1, dan while (2)mengevaluasi konstanta integer 2; dalam kedua kasus hasilnya dibandingkan untuk kesetaraan ke nol. Standar bahasa tidak mengatakan apa-apa tentang kinerja relatif dari dua konstruksi.

Saya dapat membayangkan bahwa kompiler yang sangat naif mungkin menghasilkan kode mesin yang berbeda untuk dua bentuk, setidaknya ketika dikompilasi tanpa meminta optimasi.

Di sisi lain, kompiler C mutlak harus mengevaluasi beberapa ekspresi konstan pada waktu kompilasi, ketika mereka muncul dalam konteks yang memerlukan ekspresi konstan. Sebagai contoh, ini:

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

membutuhkan diagnostik; sebuah compiler malas tidak memiliki opsi untuk menunda evaluasi 2+2sampai waktu eksekusi. Karena kompiler harus memiliki kemampuan untuk mengevaluasi ekspresi konstan pada waktu kompilasi, tidak ada alasan yang baik untuk itu tidak memanfaatkan kemampuan itu bahkan ketika itu tidak diperlukan.

Standar C ( N1570 6.8.5p4) mengatakan itu

Pernyataan iterasi menyebabkan pernyataan yang disebut loop body untuk dieksekusi berulang kali hingga ekspresi pengendali sebanding dengan 0.

Jadi, ekspresi konstan yang relevan adalah 1 == 0dan 2 == 0, yang keduanya dievaluasi intnilainya 0. (Perbandingan ini tersirat dalam semantik whileloop; mereka tidak ada sebagai ekspresi C aktual.)

Kompiler yang terlalu naif dapat menghasilkan kode yang berbeda untuk kedua konstruksi. Misalnya, untuk yang pertama dapat menghasilkan loop tak terbatas tanpa syarat (memperlakukan 1sebagai kasus khusus), dan untuk yang kedua dapat menghasilkan perbandingan run-time eksplisit setara dengan 2 != 0. Tetapi saya belum pernah menemukan kompiler C yang benar-benar akan berperilaku seperti itu, dan saya benar-benar ragu bahwa kompiler seperti itu ada.

Kebanyakan kompiler (saya tergoda untuk mengatakan semua kompiler berkualitas produksi) memiliki opsi untuk meminta optimasi tambahan. Di bawah opsi seperti itu, bahkan lebih kecil kemungkinannya bahwa kompiler akan menghasilkan kode yang berbeda untuk dua bentuk.

Jika kompiler Anda membuat kode yang berbeda untuk kedua konstruk, pertama periksa apakah urutan kode yang berbeda benar-benar memiliki kinerja yang berbeda. Jika ya, coba kompilasi lagi dengan opsi optimisasi (jika tersedia). Jika masih berbeda, kirim laporan bug ke vendor kompiler. Ini bukan (tentu saja) bug dalam arti kegagalan untuk memenuhi standar C, tetapi hampir pasti masalah yang harus diperbaiki.

Intinya: while (1)dan while(2) hampir pasti memiliki kinerja yang sama. Mereka memiliki semantik yang sama persis, dan tidak ada alasan yang baik untuk kompiler untuk tidak menghasilkan kode yang sama.

Dan meskipun kompiler sah untuk menghasilkan kode lebih cepat while(1)daripada untuk while(2), kompilator sama legalnya untuk menghasilkan kode lebih cepat while(1)daripada untuk kejadian lain while(1)dalam program yang sama.

(Ada pertanyaan lain yang tersirat dalam pertanyaan yang Anda ajukan: Bagaimana Anda berurusan dengan pewawancara yang bersikeras pada poin teknis yang salah. Itu mungkin akan menjadi pertanyaan yang bagus untuk situs Workplace ).

Keith Thompson
sumber
8
"Dalam kasus ini, ekspresi konstan yang relevan (implisit) adalah 1! = 0 dan 2! = 0, keduanya dievaluasi dengan nilai int 1" ... Ini terlalu rumit, dan tidak akurat. Standar hanya mengatakan bahwa ekspresi pengontrol whileharus dari tipe skalar dan loop body diulangi sampai ekspresi sebanding dengan 0. Ia tidak mengatakan bahwa ada implisit expr != 0yang dievaluasi ... yang akan memerlukan hasil dari itu - 0 atau 1 - pada gilirannya dibandingkan dengan 0, tak terhingga. Tidak, ekspresi dibandingkan dengan 0, tetapi perbandingan itu tidak menghasilkan nilai. PS saya terbalik.
Jim Balter
3
@ JimBalter: Saya mengerti maksud Anda, dan saya akan memperbarui jawaban saya untuk mengatasinya. Apa yang saya maksudkan, adalah bahwa kata-kata standar "... sampai ekspresi pengendali sebanding dengan 0" menyiratkan mengevaluasi <expr> == 0; itulah yang "membandingkan sama dengan 0" berarti dalam C. Perbandingan itu adalah bagian dari semantik whileloop. Tidak ada implikasi, baik dalam standar atau dalam jawaban saya, bahwa hasilnya perlu dibandingkan 0lagi. (Seharusnya saya menulis ==daripada !=.) Tetapi bagian dari jawaban saya tidak jelas, dan saya akan memperbaruinya.
Keith Thompson
1
"" membandingkan sama dengan 0 "berarti dalam C" - tetapi bahasa itu dalam standar , bukan dalam C ... implementasi melakukan perbandingan, itu tidak menghasilkan perbandingan bahasa C. Anda menulis "keduanya mengevaluasi dengan nilai int 1" - tetapi tidak ada evaluasi seperti itu yang pernah terjadi. Jika Anda melihat kode yang dihasilkan untuk while (2), while (pointer), while (9.67), oleh yang paling naif, compiler unoptimized, Anda tidak akan melihat kode yang dihasilkan bahwa hasil 0atau 1. "Aku seharusnya menulis == daripada! =" - tidak, itu tidak masuk akal.
Jim Balter
2
@ JimBalter: Hmmm. Saya tidak bermaksud menyarankan bahwa "sebanding dengan 0" menyiratkan adanya ... == 0ekspresi C. Maksud saya adalah bahwa kedua "membandingkan sama dengan 0" yang diperlukan oleh deskripsi standar tentang whileloop dan x == 0ekspresi eksplisit secara logis menyiratkan operasi yang sama. Dan saya berpikir bahwa kompiler C naif yang menyakitkan dapat menghasilkan kode yang menghasilkan intnilai 0atau 1untuk setiap whileloop - meskipun saya tidak percaya ada kompiler yang sebenarnya sangat naif.
Keith Thompson
12
Catatan: Ini sudah menjadi pertanyaan The Workplace : workplace.stackexchange.com/questions/4314/…
Joe
141

Tunggu sebentar. Pewawancara, apakah dia terlihat seperti orang ini?

masukkan deskripsi gambar di sini

Sudah cukup buruk bahwa pewawancara sendiri telah gagal dalam wawancara ini, bagaimana jika programmer lain di perusahaan ini telah "lulus" tes ini?

Tidak. Mengevaluasi pernyataan 1 == 0dan 2 == 0 harus sama cepatnya. Kita bisa membayangkan implementasi kompiler yang buruk di mana satu mungkin lebih cepat dari yang lain. Tapi tidak ada yang baik alasan mengapa salah satu harus lebih cepat dari yang lain.

Bahkan jika ada beberapa keadaan yang tidak jelas ketika klaim itu benar, programmer tidak boleh dievaluasi berdasarkan pengetahuan hal-hal yang tidak jelas (dan dalam hal ini, menyeramkan). Jangan khawatir tentang wawancara ini, langkah terbaik di sini adalah pergi.

Penafian: Ini BUKAN kartun Dilbert asli. Ini hanyalah sebuah mashup .

janos
sumber
6
Ini akan lucu, jika itu juga mengandung jawaban atas pertanyaan itu.
Cody Grey
tidak, tetapi sungguh, kita semua dapat membayangkan dengan cukup mudah bahwa semua kompiler yang ditulis oleh perusahaan yang serius akan menghasilkan kode yang masuk akal. mari kita ambil "case yang tidak dioptimalkan" / O0, mungkin itu akan berakhir seperti yang telah diposting anatolyg. Maka pertanyaannya adalah CPU, akankah cmpoperan berjalan dalam siklus kurang membandingkan 1 ke 0 dari 2 ke 0? berapa banyak siklus yang diperlukan untuk menjalankan cmp secara umum? apakah itu variabel menurut pola bit? Apakah mereka lebih sedikit "kompleks" pola bit yang melambat cmp? Saya tidak tahu secara pribadi. Anda bisa membayangkan implementasi super idiot yang memeriksa sedikit demi sedikit dari peringkat 0 hingga n (misalnya n = 31).
v.oddou
5
Itu maksud saya juga: cmpoperan harus sama cepat untuk 1 dan 200. Mungkin kita bisa membayangkan implementasi bodoh di mana ini tidak terjadi. Tapi bisakah kita membayangkan implementasi non-idiot di mana while(1)lebih cepat dari itu while(200)? Demikian pula, jika di zaman prasejarah, satu-satunya implementasi yang tersedia adalah idiot seperti itu, haruskah kita meributkannya hari ini? Saya tidak berpikir begitu, ini adalah pembicaraan bos berambut runcing, dan permata yang nyata pada saat itu!
janos
@ v.ouddou "akankah operan cmp berjalan dalam siklus yang lebih sedikit membandingkan 1 hingga 0 dari 2 hingga 0" - Tidak. Anda harus mempelajari apa itu sebuah siklus. "Saya tidak tahu secara pribadi. Anda bisa membayangkan implementasi super idiot memeriksa sedikit demi sedikit dari peringkat 0 ke n" - atau sebaliknya, masih membuat pewawancara itu bodoh. Dan mengapa khawatir memeriksa sedikit demi sedikit? Implementasinya dapat berupa seorang pria dalam kotak yang memutuskan untuk istirahat makan siang di tengah evaluasi program Anda.
Jim Balter
79

Penjelasan Anda benar. Ini tampaknya menjadi pertanyaan yang menguji kepercayaan diri Anda di samping pengetahuan teknis.

Omong-omong, jika Anda menjawab

Kedua bagian kode sama-sama cepat, karena keduanya membutuhkan waktu tak terbatas untuk menyelesaikannya

pewawancara akan berkata

Tetapi while (1)dapat melakukan lebih banyak iterasi per detik; dapatkah Anda menjelaskan mengapa? (Ini omong kosong; menguji kepercayaan diri Anda lagi)

Jadi dengan menjawab seperti yang Anda lakukan, Anda menghemat waktu yang seharusnya tidak Anda buang untuk membahas pertanyaan buruk ini.


Berikut adalah contoh kode yang dihasilkan oleh kompiler di sistem saya (MS Visual Studio 2012), dengan optimisasi dimatikan:

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

Dengan optimisasi dihidupkan:

xxx:
    jmp xxx

Jadi kode yang dihasilkan persis sama, setidaknya dengan kompiler yang mengoptimalkan.

anatolyg
sumber
28
Kode ini benar-benar apa yang dihasilkan kompiler pada sistem saya. Saya tidak mengada-ada.
anatolyg
10
icepack "Operan untuk sementara adalah dari tipe boolean" - benar-benar omong kosong. Apakah Anda pewawancara? Saya sarankan agar Anda terbiasa dengan bahasa C dan standarnya sebelum membuat klaim seperti itu.
Jim Balter
29
"Aku tidak mengada-ada." - Tolong jangan menaruh perhatian pada icepack, yang berbicara omong kosong. C tidak memiliki tipe boolean (ia memang memiliki _Bool di stdbool.h, tapi itu tidak sama, dan semantik yang whilelama mendahuluinya) dan operan whilebukan dari boolean atau _Bool atau tipe spesifik lainnya. Operand whiledapat berupa ekspresi apa saja ... saat break pada 0 dan berlanjut pada non-0.
Jim Balter
36
"Kedua bagian kode sama-sama cepat, karena keduanya membutuhkan waktu tak terbatas untuk menyelesaikannya" membuatku memikirkan sesuatu yang menarik. Satu-satunya cara untuk mengakhiri loop tak terbatas adalah agar bit dibalik oleh partikel bermuatan atau perangkat keras gagal: mengubah pernyataan dari while (00000001) {}menjadi while (00000000) {}. Semakin banyak bit yang benar, semakin kecil peluang nilai tersebut berubah menjadi false. Sayangnya, 2 juga hanya memiliki satu bit yang benar. 3, bagaimanapun, akan berjalan secara signifikan lebih lama. Ini juga hanya berlaku untuk kompiler yang tidak selalu mengoptimalkan ini (VC ++).
Jonathan Dickinson
8
@ mr5 nggak. Agar sedikit membalik untuk benar-benar menghasilkan sesuatu seperti ini, Anda berbicara tentang waktu eksekusi dalam puluhan ribu tahun. Hanya eksperimen pikiran. Jika Anda berasal dari ras abadi Anda mungkin ingin menggunakan -1 untuk mencegah bit-flipping dari mempengaruhi program Anda.
Jonathan Dickinson
60

Penjelasan yang paling mungkin untuk pertanyaan tersebut adalah bahwa pewawancara berpikir bahwa prosesor memeriksa bit individual dari angka-angka, satu per satu, sampai menyentuh nilai yang tidak nol:

1 = 00000001
2 = 00000010

Jika "nol?" Algoritma dimulai dari sisi kanan nomor dan harus memeriksa setiap bit sampai mencapai bit non-nol, while(1) { }loop harus memeriksa dua kali lebih banyak bit per iterasi sebagai while(2) { }loop.

Ini membutuhkan model mental yang sangat salah tentang bagaimana komputer bekerja, tetapi ia memang memiliki logika internalnya sendiri. Salah satu cara untuk memeriksa adalah dengan menanyakan apakah while(-1) { }atau while(3) { }akan sama cepatnya, atau apakah while(32) { }akan lebih lambat .

Ryan Cavanaugh
sumber
39
Saya mengasumsikan kesalahpahaman pewawancara akan lebih seperti "2 adalah int yang perlu dikonversi ke boolean agar dapat digunakan dalam ekspresi bersyarat, sedangkan 1 sudah boolean."
Russell Borogove
7
Dan jika algoritma perbandingan dimulai dari kiri, itu sebaliknya.
Paŭlo Ebermann
1
+1, itulah yang saya pikirkan. Anda dapat dengan sempurna membayangkan seseorang percaya bahwa pada dasarnya, cmpalgoritma dari CPU adalah pemeriksaan bit linier dengan keluar loop awal pada perbedaan pertama.
v.oddou
1
@PeterCordes "Saya belum pernah mendengar orang (selain Anda) berpendapat bahwa -O0output gcc atau dentang tidak cukup literal." Saya tidak pernah mengatakan hal seperti itu dan saya tidak memikirkan gcc atau dentang sama sekali. Anda pasti salah baca saya. Saya hanya tidak sependapat dengan Anda bahwa apa yang dihasilkan MSVC itu lucu.
blubberdiblub
1
@PeterCordes saya salah, di MSVC breakpoint pada while(1)tidak merusak sebelum loop, tetapi pada ekspresi. Tapi itu masih runtuh di dalam loop ketika mengoptimalkan ekspresi. Ambil kode ini dengan banyak istirahat. Dalam mode debugging yang tidak dioptimalkan Anda mendapatkan ini . Saat mengoptimalkan, banyak breakpoint jatuh bersama-sama atau bahkan tumpah ke fungsi berikutnya (IDE menunjukkan breakpoint hasil saat debugging) - pembongkaran yang sesuai .
blubberdiblub
32

Tentu saja saya tidak tahu maksud sebenarnya dari manajer ini, tetapi saya mengusulkan pandangan yang sama sekali berbeda: Ketika merekrut anggota baru ke dalam tim, akan berguna untuk mengetahui bagaimana dia bereaksi terhadap situasi konflik.

Mereka mendorong Anda ke dalam konflik. Jika ini benar, mereka pintar dan pertanyaannya bagus. Untuk beberapa industri, seperti perbankan, memposting masalah Anda ke Stack Overflow bisa menjadi alasan penolakan.

Tapi tentu saja saya tidak tahu, saya hanya mengusulkan satu opsi.

Tõnu Samuel
sumber
Memang sangat bagus tapi sementara (2) vs sementara (1) jelas diambil dari komik dilbert. TIDAK DAPAT ditemukan oleh seseorang yang waras (bagaimana orang bisa membuat sementara (2) sebagai hal yang mungkin untuk ditulis?). Jika hipotesis Anda benar, pasti Anda akan memberikan masalah yang sangat unik sehingga Anda dapat menggunakan google untuk itu. Seperti "apakah sementara (0xf00b442) lebih lambat dari pada saat (1)", bagaimana bank akan menemukan pertanyaan penerima wawancara sebaliknya? apakah Anda mengira mereka adalah NSA dan memiliki akses ke keycore?
v.oddou
25

Saya pikir petunjuknya dapat ditemukan di "diminta oleh manajer senior". Orang ini jelas berhenti pemrograman ketika ia menjadi manajer dan kemudian butuh beberapa tahun untuk menjadi manajer senior. Tidak pernah kehilangan minat dalam pemrograman, tetapi tidak pernah menulis baris sejak saat itu. Jadi rujukannya bukanlah "kompiler yang layak di luar sana" seperti beberapa jawaban menyebutkan, tetapi "kompiler yang digunakan orang ini 20-30 tahun yang lalu".

Pada saat itu, programmer menghabiskan banyak waktu mereka untuk mencoba berbagai metode untuk membuat kode mereka lebih cepat dan lebih efisien karena waktu CPU dari 'komputer mini sentral' sangat berharga. Seperti yang dilakukan orang menulis kompiler. Saya menduga bahwa satu-satunya kompiler yang disediakan perusahaannya pada waktu itu dioptimalkan berdasarkan 'pernyataan yang sering ditemui yang dapat dioptimalkan' dan mengambil sedikit jalan pintas ketika bertemu beberapa saat (1) dan mengevaluasi semuanya lain, termasuk beberapa saat (2). Memiliki pengalaman seperti itu dapat menjelaskan posisi dan kepercayaan dirinya terhadapnya.

Pendekatan terbaik untuk mempekerjakan Anda mungkin adalah pendekatan yang memungkinkan manajer senior terbawa suasana dan memberi kuliah Anda 2-3 menit tentang "hari-hari baik pemrograman" sebelum ANDA dengan lancar membawanya ke subjek wawancara berikutnya. (Pengaturan waktu yang baik penting di sini - terlalu cepat dan Anda menginterupsi ceritanya - terlalu lambat dan Anda dicap sebagai orang yang kurang fokus). Katakan padanya di akhir wawancara bahwa Anda akan sangat tertarik untuk mempelajari lebih lanjut tentang topik ini.

OldFrank
sumber
19

Anda seharusnya bertanya kepadanya bagaimana dia mencapai kesimpulan itu. Di bawah setiap kompiler yang layak di luar sana, keduanya mengkompilasi dengan instruksi asm yang sama. Jadi, dia seharusnya memberi tahu Anda kompiler untuk memulai. Dan meskipun demikian, Anda harus mengetahui kompiler dan platform dengan sangat baik untuk membuat tebakan teoritis. Dan pada akhirnya, itu tidak terlalu penting dalam praktik, karena ada faktor-faktor eksternal lainnya seperti fragmentasi memori atau beban sistem yang akan mempengaruhi loop lebih dari detail ini.

Valentin Radu
sumber
14
@GKFX Jika Anda telah memberikan jawaban Anda dan mereka mengatakan bahwa Anda salah, tidak ada alasan Anda tidak dapat meminta mereka untuk menjelaskan alasannya. Jika Anatolyg benar dan ini merupakan ujian kepercayaan diri Anda, maka Anda harus menjelaskan mengapa Anda menjawab seperti itu dan menanyakan hal yang sama kepada mereka.
P.Turpie
Maksud saya sebagai hal pertama yang Anda katakan kepada mereka. Tidak mungkin "Mengapa x lebih cepat?" "Saya tidak tahu; mengapa x lebih cepat?" Jelas, setelah menjawab dengan benar, Anda bisa bertanya.
GKFX
18

Demi pertanyaan ini, saya harus menambahkan bahwa saya ingat Doug Gwyn dari Komite C menulis bahwa beberapa kompiler C awal tanpa pass pengoptimal akan menghasilkan tes dalam perakitan untuk while(1)(dibandingkan dengan for(;;)yang tidak memilikinya).

Saya akan menjawab kepada pewawancara dengan memberikan catatan sejarah ini dan kemudian mengatakan bahwa bahkan jika saya akan sangat terkejut setiap kompiler melakukan ini, kompiler dapat memiliki:

  • tanpa pengoptimal yang lulus, kompiler menghasilkan tes untuk keduanya while(1)danwhile(2)
  • dengan pass pengoptimal kompiler diinstruksikan untuk mengoptimalkan (dengan lompatan tanpa syarat) semua while(1)karena mereka dianggap sebagai idiomatik. Ini akan meninggalkan while(2)dengan tes dan karenanya membuat perbedaan kinerja antara keduanya.

Saya tentu saja akan menambahkan kepada pewawancara yang tidak mempertimbangkan while(1)dan while(2)membangun yang sama adalah tanda optimasi berkualitas rendah karena ini adalah konstruksi yang setara.

ouah
sumber
10

Cara lain untuk pertanyaan seperti itu adalah untuk melihat apakah Anda punya keberanian untuk memberi tahu manajer Anda bahwa dia salah! Dan seberapa lembut Anda bisa mengomunikasikannya.

Insting pertama saya adalah menghasilkan output perakitan untuk menunjukkan kepada manajer bahwa setiap kompiler yang layak harus mengatasinya, dan jika tidak melakukannya, Anda akan mengirimkan tambalan berikutnya untuk itu :)

UncleKing
sumber
8

Untuk melihat begitu banyak orang mempelajari masalah ini, perlihatkan dengan tepat mengapa ini bisa menjadi ujian untuk melihat seberapa cepat Anda ingin mengoptimalkan berbagai hal secara mikro .

Jawaban saya adalah; tidak masalah, saya lebih fokus pada masalah bisnis yang sedang kita selesaikan. Bagaimanapun, itulah yang akan saya bayar.

Selain itu, saya akan memilih while(1) {}karena itu lebih umum, dan rekan satu tim lainnya tidak perlu menghabiskan waktu untuk mencari tahu mengapa seseorang memilih angka yang lebih tinggi dari 1.

Sekarang, tulis beberapa kode. ;-)

Bart Verkoeijen
sumber
1
Kecuali Anda dibayar untuk mengoptimalkan beberapa kode waktu nyata yang Anda butuhkan untuk mencukur hanya 1 atau 2 milidetik agar sesuai dengan tuntutan waktu berjalannya. Tentu saja itu pekerjaan untuk optimizer, beberapa orang akan mengatakan - itu adalah jika Anda memiliki optimizer untuk arsitektur Anda.
Neowizard
6

Jika Anda begitu khawatir tentang pengoptimalan, Anda harus menggunakannya

for (;;)

karena itu tidak memiliki tes. (mode sinis)

Tom Tanner
sumber
2
Itu sebabnya bijak untuk digunakan while(2), ia meninggalkan ruang untuk optimasi, maka Anda cukup beralih ke for (;;)ketika Anda siap untuk meningkatkan kinerja.
Groo
5

Menurut saya ini adalah salah satu pertanyaan wawancara perilaku yang disamarkan sebagai pertanyaan teknis. Beberapa perusahaan melakukan ini - mereka akan mengajukan pertanyaan teknis yang seharusnya cukup mudah dijawab oleh programmer mana pun yang kompeten, tetapi ketika orang yang diwawancarai memberikan jawaban yang benar, pewawancara akan memberi tahu mereka bahwa mereka salah.

Perusahaan ingin melihat bagaimana Anda akan bereaksi dalam situasi ini. Apakah Anda duduk di sana dengan tenang dan tidak memaksakan jawaban Anda benar, karena keraguan diri atau takut mengganggu pewawancara? Atau apakah Anda bersedia menantang seseorang yang memiliki otoritas yang Anda tahu salah? Mereka ingin melihat apakah Anda bersedia membela keyakinan Anda, dan jika Anda bisa melakukannya dengan cara yang bijaksana dan penuh hormat.

aliran balik pacover
sumber
3

Saya dulu memprogram kode C dan Majelis kembali ketika omong kosong semacam ini mungkin membuat perbedaan. Ketika hal itu membuat perbedaan, kami menulisnya di Majelis.

Jika saya ditanya pertanyaan itu, saya akan mengulangi kutipan terkenal Donald Knuth tahun 1974 tentang pengoptimalan prematur dan berjalan jika pewawancara tidak tertawa dan melanjutkan.

Peter Wooster
sumber
1
Mengingat dia juga mengatakan "Dalam disiplin ilmu teknik yang mapan, peningkatan 12%, mudah diperoleh, tidak pernah dianggap marjinal dan saya percaya sudut pandang yang sama harus berlaku dalam rekayasa perangkat lunak", saya pikir Anda berjalan tidak dapat dibenarkan.
Alice
3

Mungkin pewawancara mengajukan pertanyaan bodoh seperti itu dengan sengaja dan ingin Anda membuat 3 poin:

  1. Alasan dasar. Kedua loop tidak terbatas, sulit untuk berbicara tentang kinerja.
  2. Pengetahuan tentang level pengoptimalan. Dia ingin mendengar dari Anda jika Anda membiarkan kompiler melakukan optimasi untuk Anda, itu akan mengoptimalkan kondisi, terutama jika blok tidak kosong.
  3. Pengetahuan tentang arsitektur mikroprosesor. Sebagian besar arsitektur memiliki instruksi CPU khusus untuk perbandingan dengan 0 (sementara tidak harus lebih cepat).
Rok Kralj
sumber
2

Inilah masalahnya: Jika Anda benar-benar menulis sebuah program dan mengukur kecepatannya, kecepatan kedua loop bisa berbeda! Untuk perbandingan yang masuk akal:

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

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

dengan beberapa kode ditambahkan yang mencetak waktu, beberapa efek acak seperti bagaimana loop diposisikan dalam satu atau dua baris cache dapat membuat perbedaan. Satu loop mungkin kebetulan sepenuhnya dalam satu baris cache, atau pada awal baris cache, atau mungkin untuk mengangkangi dua baris cache. Dan sebagai hasilnya, apa pun yang diklaim pewawancara paling cepat mungkin sebenarnya tercepat - secara kebetulan.

Skenario kasus terburuk: Kompilator pengoptimal tidak mengetahui apa yang dilakukan loop, tetapi memperkirakan bahwa nilai yang dihasilkan saat loop kedua dieksekusi adalah yang sama dengan yang dihasilkan oleh yang pertama. Dan menghasilkan kode lengkap untuk loop pertama, tetapi tidak untuk yang kedua.

gnasher729
sumber
1
Alasan "garis cache" hanya akan bekerja pada chip yang memiliki cache sangat kecil.
sharptooth
10
Ini intinya. Pertanyaan tentang kecepatan mengasumsikan "semuanya sama".
Jim Balter
6
@ JimBalter: Ini bukan pertanyaan tentang kecepatan, itu adalah pertanyaan tentang argumen dengan pewawancara. Dan kemungkinan bahwa melakukan tes yang sebenarnya mungkin membuktikan pewawancara itu "benar" - untuk alasan yang tidak ada hubungannya dengan argumennya tetapi merupakan kebetulan murni. Yang akan menempatkan Anda dalam situasi yang memalukan jika Anda mencoba membuktikan bahwa dia salah.
gnasher729
1
@ gnasher729 Di samping semua argumen logis, kasus kebetulan yang Anda bicarakan pantas untuk dilihat. Tapi masih membabi buta percaya bahwa kasus seperti itu ada bukan hanya naif tetapi juga bodoh. Selama Anda tidak menjelaskan apa, bagaimana atau mengapa itu akan terjadi, jawaban ini tidak berguna.
user568109
4
@ gnasher729 "Ini bukan masalah kecepatan" - Saya tidak akan berdebat dengan orang yang menggunakan teknik retorika yang tidak jujur.
Jim Balter
2

Keduanya sama - sama.

Menurut spesifikasi apa pun yang bukan 0 dianggap benar, bahkan tanpa optimasi apa pun, dan kompiler yang baik tidak akan menghasilkan kode apa pun untuk sementara (1) atau sementara (2). Kompiler akan menghasilkan pemeriksaan sederhana untuk != 0.

Nikolay Ivanchev
sumber
@djechlin - karena keduanya mengambil siklus 1 cpu.
UncleKing
Konstanta kompiler melipatnya, sehingga bahkan tidak dievaluasi pada saat run time.
PaulHK
2

Menilai dari jumlah waktu dan upaya yang dihabiskan orang untuk menguji, membuktikan, dan menjawab pertanyaan yang sangat langsung ini, saya mengatakan bahwa keduanya dibuat sangat lambat dengan mengajukan pertanyaan.

Dan untuk menghabiskan lebih banyak waktu untuk itu ...

"Sementara (2)" konyol, karena,

"while (1)", dan "while (true)" secara historis digunakan untuk membuat loop tak terbatas yang mengharapkan "break" untuk dipanggil pada beberapa tahap di dalam loop berdasarkan kondisi yang pasti akan terjadi.

"1" hanya ada di sana untuk selalu mengevaluasi ke benar dan karena itu, untuk mengatakan "sementara (2)" sama konyolnya dengan mengatakan "sementara (1 + 1 == 2)" yang juga akan mengevaluasi ke benar.

Dan jika Anda ingin benar-benar konyol, gunakan saja: -

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

Saya ingin berpikir bahwa pembuat kode Anda membuat kesalahan ketik yang tidak mempengaruhi jalannya kode, tetapi jika dia sengaja menggunakan "2" hanya untuk menjadi aneh, maka pindahkan dia sebelum dia memasukkan semua file Anda ke kode Anda sehingga sulit untuk baca dan bekerja dengan.

ekerner
sumber
Kembalikan yang Anda buat edit kembali oleh pengguna yang sangat tinggi. Orang-orang ini biasanya mengetahui kebijakan dengan sangat baik dan mereka ada di sini untuk mengajarkan mereka kepada Anda. Saya menemukan baris terakhir dari posting Anda menambahkan tidak ada yang berguna untuk posting Anda. Bahkan jika saya mendapat lelucon saya jelas hilang, saya akan menganggapnya terlalu cerewet, tidak layak paragraf tambahan.
Palec
@Palec: Terima kasih atas komentarnya. Saya menemukan bahwa orang yang sama mengedit banyak posting saya, sebagian besar hanya untuk menghapus tanda off. Jadi saya pikir dia hanya troll reputasi, dan karenanya reputasinya. Sudahkah forum online menghabiskan bahasa - dan sapa umum - sedemikian rupa sehingga kita tidak lagi menandatangani tulisan kita? mungkin saya sedikit sekolah tua tapi sepertinya tidak sopan untuk menghilangkan halo dan selamat tinggal. Kami menggunakan aplikasi, tetapi kami masih manusia.
ekerner
1
Di Stack Exchange , salam, tagline, terima kasih, dll. Tidak diterima . Hal yang sama disebutkan di pusat bantuan , khususnya di bawah perilaku yang diharapkan . Tidak ada bagian dari Stack Exchange , bahkan Stack Overflow , yang merupakan forum - mereka adalah situs T&J. Mengedit adalah salah satu perbedaannya. Juga komentar harus berfungsi hanya untuk memberikan umpan balik pada posting, bukan untuk diskusi ( Stack Overflow Chat ). Dan ada banyak cara lain T&J berbeda dari forum. Lebih lanjut tentang itu di pusat bantuan dan pada Meta Stack Overflow .
Palec
Perhatikan bahwa ketika Anda memiliki lebih dari 2000 rep (hak istimewa edit), Anda tidak akan mendapatkan rep lagi dari suntingan. Jika ragu mengapa dan pengeditan yang tidak Anda setujui telah diterapkan ke pos Anda, atau Anda melihat perilaku salah seseorang, tanyakan di Meta Stack Overflow . Jika editor melakukan hal yang salah, mereka dapat diberi tahu oleh mod (mungkin mereka tidak menyadari) atau bahkan mendapatkan hukuman entah bagaimana (jika perilaku itu sengaja berbahaya). Kalau tidak, Anda mendapatkan petunjuk untuk penjelasan mengapa edit / perilaku itu benar. Rollback clutters revisi riwayat yang tidak perlu dan dapat membawa Anda ke perang rollback. Saya memutar kembali hanya ketika benar-benar yakin hasil edit salah.
Palec
Akhirnya tentang penandatanganan, salam, ...: Tampaknya tidak sopan untuk tidak memasukkan formalitas ini, tetapi meningkatkan rasio sinyal terhadap kebisingan dan tidak ada informasi yang hilang. Anda dapat memilih nama panggilan yang Anda inginkan, yaitu tanda tangan Anda. Di sebagian besar tempat, Anda memiliki avatar juga.
Palec
1

Itu tergantung pada kompiler.

Jika mengoptimalkan kode, atau jika mengevaluasi 1 dan 2 menjadi benar dengan jumlah instruksi yang sama untuk set instruksi tertentu, kecepatan eksekusi akan sama.

Dalam kasus nyata itu akan selalu sama cepatnya, tetapi akan mungkin untuk membayangkan kompiler tertentu dan sistem tertentu ketika ini akan dievaluasi secara berbeda.

Maksud saya: ini bukan pertanyaan terkait bahasa (C).

pengguna143522
sumber
0

Karena orang yang ingin menjawab pertanyaan ini menginginkan loop tercepat, saya akan menjawab bahwa keduanya sama-sama menyusun kode perakitan yang sama, seperti yang dinyatakan dalam jawaban lain. Namun demikian Anda dapat menyarankan kepada pewawancara menggunakan 'loop membuka gulungan'; a do {} while bukan loop while.

Hati-hati: Anda perlu memastikan bahwa loop setidaknya akan selalu berjalan sekali .

Lingkaran harus memiliki kondisi istirahat di dalamnya.

Juga untuk loop semacam itu, saya pribadi lebih suka menggunakan do {} while (42) karena bilangan bulat apa pun, kecuali 0, akan melakukan pekerjaan itu.

Antonin GAVREL
sumber
Mengapa ia menyarankan do {} while? Sementara (1) (atau sementara (2)) melakukan hal yang sama.
Catsunami
lakukan sambil menghapus satu lompatan ekstra sehingga kinerja lebih baik. Tentu saja dalam kasus di mana Anda tahu bahwa loop akan dieksekusi setidaknya sekali
Antonin GAVREL
0

Jawaban yang jelas adalah: seperti yang diposting, kedua fragmen akan menjalankan loop infinite yang sama sibuknya, yang membuat program menjadi sangat lambat .

Meskipun mendefinisikan ulang kata kunci C sebagai makro secara teknis akan memiliki perilaku yang tidak terdefinisi, itu adalah satu-satunya cara yang dapat saya pikirkan untuk membuat salah satu kode fragmen dengan cepat: Anda dapat menambahkan baris ini di atas 2 fragmen:

#define while(x) sleep(x);

memang akan membuat while(1)dua kali lebih cepat (atau setengah lambat) while(2).

chqrlie
sumber
@ MaxB: memang trik makro harus lebih diperdebatkan. Saya pikir versi baru harus berfungsi, meskipun tidak dijamin oleh Standar C.
chqrlie
-4

Satu-satunya alasan saya bisa memikirkan mengapa hal while(2)itu menjadi lebih lambat adalah:

  1. Kode mengoptimalkan perulangan ke

    cmp eax, 2

  2. Ketika pengurangan terjadi, Anda pada dasarnya mengurangi

    Sebuah. 00000000 - 00000010 cmp eax, 2

    dari pada

    b. 00000000 - 00000001 cmp eax, 1

cmphanya mengeset flag dan tidak menetapkan hasilnya. Jadi pada bit paling signifikan kita tahu apakah kita perlu meminjam atau tidak dengan b . Sedangkan dengan sebuah Anda harus melakukan dua subtractions sebelum Anda mendapatkan meminjam a.

hdost
sumber
15
cmp akan mengambil 1 siklus cpu terlepas dari kedua kasus.
UncleKing
8
Kode itu akan salah. Kode yang benar akan memuat 2 atau 1 ke register eax, kemudian membandingkan eax dengan 0.
gnasher729