Seberapa banyak penggunaan makro "kemungkinan" dan "tidak mungkin"?

12

Makro likelydan unlikelymakro sering dikenal membantu kompiler mengetahui apakah suatu ifbiasanya akan dimasukkan atau dilewati. Menggunakannya menghasilkan beberapa peningkatan kinerja (agak kecil).

Saya mulai menggunakannya baru-baru ini, dan saya tidak yakin seberapa sering petunjuk seperti itu digunakan. Saat ini saya menggunakannya dengan pengecekan kesalahan if, yang biasanya ditandai sebagai unlikely. Sebagai contoh:

mem = malloc(size);
if (unlikely(mem == NULL))
  goto exit_no_mem;

Tampaknya baik-baik saja, tetapi pengecekan kesalahan ifterjadi cukup sering dan akibatnya penggunaan makro tersebut.

Pertanyaan saya adalah, apakah terlalu banyak untuk dimiliki likelydan unlikelymakro pada setiap pemeriksaan kesalahan if?

Sementara kita berada di sana, tempat lain apa yang sering mereka gunakan?


Dalam penggunaan saya saat ini di perpustakaan yang membuat abstraksi dari subsistem real-time, jadi program akan menjadi portabel antara RTAI, QNX dan lainnya. Yang mengatakan, sebagian besar fungsi agak kecil dan langsung memanggil satu atau dua fungsi lainnya. Banyak yang bahkan static inlineberfungsi.

Jadi, pertama-tama, ini bukan aplikasi yang bisa saya profil. Tidak masuk akal untuk "mengidentifikasi leher botol" karena itu adalah perpustakaan, bukan aplikasi mandiri.

Kedua, ini seperti "Saya tahu ini tidak mungkin, saya mungkin juga mengatakannya kepada kompiler". Saya tidak secara aktif mencoba mengoptimalkan if.

Shahbaz
sumber
7
berbau mikro mengoptimalkan bagi saya ...
ratchet freak
2
Untuk kode aplikasi, saya akan menambahkannya hanya jika profil menunjukkan bahwa kode ini digunakan di jalur panas.
CodesInChaos
@ames, itu hanya mengatakan likelydan unlikelyada dan apa yang mereka lakukan. Saya tidak menemukan apa pun yang benar-benar menyarankan kapan dan di mana cara terbaik untuk menggunakannya.
Shahbaz
@ Shahbaz "Jika kondisinya sering salah, eksekusi tidak linier. Ada sejumlah besar kode yang tidak digunakan di tengah yang tidak hanya mencemari L1i karena prefetching, itu juga dapat menyebabkan masalah dengan prediksi cabang. Jika Prediksi cabang salah, ekspresi kondisional bisa sangat tidak efisien. " Jadi, loop ketat di mana Anda ingin memastikan instruksi yang Anda butuhkan ada dalam cache L1i
James

Jawaban:

12

Apakah Anda memerlukan kinerja yang sangat buruk sehingga Anda bersedia mencemari kode Anda dengan itu? Ini optimasi kecil.

  • Apakah kode berjalan dalam loop ketat?
  • Apakah aplikasi Anda memiliki masalah kinerja?
  • Sudahkah Anda membuat profil aplikasi dan menetapkan bahwa perulangan khusus ini menghabiskan banyak waktu CPU?

Kecuali Anda dapat menjawab yessemua hal di atas, jangan repot-repot dengan hal-hal seperti ini.

Sunting: sebagai tanggapan terhadap sunting. Bahkan ketika Anda tidak dapat membuat profil, Anda biasanya dapat memperkirakan hotspot. Fungsi alokasi memori yang dipanggil oleh semua orang adalah kandidat yang baik, terutama karena hanya membutuhkan satu penggunaan makro untuk bekerja untuk seluruh perpustakaan.

Pria jawa
sumber
1
Untuk lebih jelasnya, saya tidak memilih Anda. Namun, jawaban Anda tidak benar-benar menjawab pertanyaan saya. Apakah Anda mencoba mengatakan bahwa (un)likelymakro jarang digunakan dan hanya dalam kode kritis kinerja sangat? Apakah "praktik buruk" sering menggunakannya, atau hanya "tidak perlu"?
Shahbaz
@ Shahbaz Itu membuat kode kurang dibaca dan optimasi kinerja dapat berkisar dari keuntungan sepele untuk kerugian sepele. Yang terakhir ketika asumsi tentang kemungkinan salah atau telah berubah menjadi salah karena perubahan kemudian ke bagian lain dari kode. Jika tidak boleh digunakan kecuali diperlukan.
Peter
3
@ Peter: Meskipun terlalu buruk sintaksisnya tidak lebih baik, notasi tentang apa yang mungkin atau tidak mungkin dapat memberikan informasi yang berguna bagi manusia yang membaca kode. Sebagai contoh, seseorang yang melihat if (likely(x==2 || x==3)) doOneThing(); else switch(x) { ... }, mungkin menilai bahwa penggunaan programer ifuntuk nilai 2 dan 3 bukan hanya konsekuensi dari programmer yang tidak mengetahui bahwa C dapat mengaitkan dua caselabel dengan handler tunggal.
supercat
Tidak ada yang menyebutkan apa yang saya rasakan sebagai poin kritis. Bukan hanya bahwa jalan "tidak mungkin" lebih jarang terjadi, mungkin saja jalan yang dikondisikan pada jalan itu terjadi sehingga Anda tidak peduli dengan kecepatan sama sekali. Misalnya, periferal menjadi tidak responsif sehingga Anda harus meresetnya dan tetap tidur.
Benjamin Lindqvist
2

Jika Anda menulis untuk x86 / x64 (dan tidak menggunakan CPU berusia 20 tahun), keuntungan kinerja dari menggunakan __builtin_expect () akan diabaikan jika ada. Alasannya adalah bahwa CPU x86 / x64 modern (tidak 100% yakin tentang Atom), memiliki prediksi cabang dinamis, jadi pada dasarnya CPU "belajar" tentang cabang yang diambil lebih sering. Tentu, informasi ini dapat disimpan hanya untuk sejumlah cabang, namun, hanya ada dua kasus yang mungkin. Jika (a) itu cabang "sering digunakan", maka program Anda akan mendapat manfaat dari prediksi cabang dinamis itu, dan jika (b) itu cabang "langka", Anda tidak akan benar-benar melihat hit kinerja yang realistis karena salah duga di cabang langka seperti itu (20 siklus CPU misprediksi cabang tidak terlalu buruk jika terjadi sekali di bulan biru).

NB: ini TIDAK menyiratkan bahwa pada x86 / x64 modern pentingnya misprediksi cabang menjadi lebih rendah: cabang mana pun dengan peluang 50-50 melompat-nojump akan tetap dikenai penalti (siklus CPU IIRC 10-20), jadi dalam loop internal cabang mungkin masih perlu dihindari. Hanya pentingnya __builtin_expect () pada x86 / x64 yang telah berkurang (IIRC, sekitar 10-15 tahun yang lalu atau lebih) - sebagian besar karena prediksi cabang dinamis.

NB2: untuk platform lain di luar x86 / x64, YMMV.

Kelinci Tanpa Bug
sumber
Nah, kompiler tahu cabang mana yang cpu akan perkirakan kecil kemungkinannya. Dan itu bisa mengatur untuk menjadi yang sebenarnya tidak mungkin. Tetapi kompiler mungkin sudah tahu pola itu tanpa unlikely-notasi.
Deduplicator
@Dupuplikator: dengan prediksi cabang dinamis, kompiler tidak tahu cabang mana yang lebih mungkin karena CPU menghitungnya dalam runtime berdasarkan run sebelumnya melalui titik ini dalam kode.
No-Bugs Hare