Pada tingkat yang sangat rendah (di hardware), ya, jika s yang mahal. Untuk memahami alasannya, Anda harus memahami cara kerja pipeline .
Instruksi saat ini untuk dieksekusi disimpan dalam sesuatu yang biasanya disebut pointer instruksi (IP) atau program counter (PC); istilah-istilah ini sama, tetapi istilah yang berbeda digunakan untuk arsitektur yang berbeda. Untuk kebanyakan instruksi, PC instruksi berikutnya hanyalah PC saat ini ditambah panjang instruksi saat ini. Untuk kebanyakan arsitektur RISC, semua instruksi memiliki panjang yang konstan, sehingga PC dapat bertambah dengan jumlah yang konstan. Untuk arsitektur CISC seperti x86, instruksi dapat memiliki panjang variabel, sehingga logika yang menerjemahkan instruksi harus mencari tahu berapa lama instruksi saat ini untuk menemukan lokasi instruksi berikutnya.
Untuk instruksi cabang , bagaimanapun, instruksi selanjutnya yang akan dieksekusi bukanlah lokasi berikutnya setelah instruksi saat ini. Cabang adalah gotos - mereka memberi tahu prosesor di mana instruksi berikutnya. Cabang dapat bersyarat atau tidak bersyarat, dan lokasi target dapat ditetapkan atau dihitung.
Bersyarat vs. tak bersyarat mudah dipahami - cabang bersyarat hanya diambil jika kondisi tertentu berlaku (seperti apakah satu angka sama dengan yang lain); jika cabang tidak diambil, kontrol melanjutkan ke instruksi berikutnya setelah cabang seperti biasa. Untuk cabang tanpa syarat, cabang selalu diambil. Cabang bersyarat muncul dalam if
pernyataan dan tes kontrol for
dan while
loop. Cabang tanpa syarat muncul di loop tak terbatas, pemanggilan fungsi, pengembalian fungsi, break
dan continue
pernyataan, goto
pernyataan terkenal , dan banyak lagi (daftar ini jauh dari lengkap).
Target cabang adalah masalah penting lainnya. Sebagian besar cabang memiliki target cabang tetap - mereka pergi ke lokasi tertentu dalam kode yang ditetapkan pada waktu kompilasi. Ini termasuk if
pernyataan, loop dari segala jenis, pemanggilan fungsi reguler, dan banyak lagi. Dihitung cabang menghitung target cabang di runtime. Ini termasuk switch
pernyataan (terkadang), kembali dari suatu fungsi, panggilan fungsi virtual, dan panggilan penunjuk fungsi.
Jadi, apa artinya semua ini bagi kinerja? Saat prosesor melihat instruksi cabang muncul di pipeline-nya, ia perlu mencari cara untuk terus mengisi pipeline-nya. Untuk mengetahui instruksi apa yang muncul setelah cabang dalam aliran program, perlu diketahui dua hal: (1) apakah cabang tersebut akan diambil dan (2) target dari cabang tersebut. Mencari tahu ini disebut prediksi cabang , dan ini adalah masalah yang menantang. Jika prosesor menebak dengan benar, program berlanjut dengan kecepatan penuh. Jika sebaliknya, prosesor menebak dengan salah , itu hanya menghabiskan beberapa waktu untuk menghitung hal yang salah. Sekarang harus membersihkan pipeline dan memuatnya kembali dengan instruksi dari jalur eksekusi yang benar. Intinya: kinerja besar yang sukses.
Jadi, alasan mengapa jika pernyataan mahal adalah karena kesalahan prediksi cabang . Ini hanya di level terendah. Jika Anda menulis kode tingkat tinggi, Anda tidak perlu mengkhawatirkan detail ini sama sekali. Anda seharusnya hanya peduli tentang ini jika Anda menulis kode yang sangat penting untuk kinerja di C atau assembly. Jika demikian, menulis kode bebas-cabang sering kali lebih baik daripada kode yang bercabang, bahkan jika diperlukan beberapa instruksi lagi. Ada beberapa keren bit-memutar-mutar trik yang dapat Anda lakukan untuk menghitung hal-hal seperti abs()
, min()
, dan max()
tanpa bercabang.