Kapan harus menggunakan fungsi sebaris dan kapan tidak menggunakannya?

185

Saya tahu bahwa inline adalah petunjuk atau permintaan untuk mengkompilasi dan digunakan untuk menghindari overhead panggilan fungsi.

Jadi atas dasar apa seseorang dapat menentukan apakah suatu fungsi adalah kandidat untuk inlining atau tidak? Dalam hal mana seseorang harus menghindari inlining?

Ashish
sumber
11
inlineadalah untuk pendatang baru C ++ apa CFLAGSyang menjadi pendatang baru Gentoo: tidak, kompilasi dengan -O3 -funroll-loops -finline-functionstidak akan membuat Pentium lama Anda terbang;)
Gregory Pakosz
1
Alasan untuk tidak menggunakan inline adalah bahwa beberapa debugger tidak akan memungkinkan Anda untuk mengatur break point atau melangkah ke fungsi inline.
Rob deFriesse
1
Duplikat: stackoverflow.com/questions/1875947/…
Steve Jessop
5
Anda seharusnya tidak menentukan apakah suatu fungsi harus diuraikan atau tidak. Biarkan kompiler melakukannya; itu lebih baik daripada Anda (dan dapat inline fungsi selektif berdasarkan lingkungan dari setiap panggilan).
David Thornley
@ Davidvidhorn kadang-kadang, bahkan dengan flag O3 ditetapkan, kompiler tidak inline fungsi jika definisi ada dalam file cpp. Jadi, aturan praktis yang saya ikuti adalah inline satu liner dan juga fungsi-fungsi itu tanpa loop.
talekeDskobeDa

Jawaban:

210

Menghindari biaya panggilan fungsi hanya setengah dari cerita.

melakukan:

  • gunakan inlinesebagai ganti#define
  • fungsi yang sangat kecil adalah kandidat yang baik untuk inline: kode yang lebih cepat dan executable yang lebih kecil (lebih banyak peluang untuk tetap dalam cache kode)
  • fungsinya kecil dan disebut sangat sering

jangan:

  • fungsi besar: mengarah ke executable lebih besar, yang secara signifikan merusak kinerja terlepas dari eksekusi lebih cepat yang dihasilkan dari panggilan overhead
  • fungsi sebaris yang terikat I / O
  • fungsi ini jarang digunakan
  • konstruktor dan destruktor: bahkan ketika kosong, kompilator menghasilkan kode untuk mereka
  • melanggar kompatibilitas biner saat mengembangkan perpustakaan:
    • sebaris fungsi yang ada
    • mengubah fungsi inline atau membuat fungsi inline non-inline: versi sebelumnya dari perpustakaan memanggil implementasi lama

saat mengembangkan perpustakaan, untuk membuat kelas dapat dikembangkan di masa mendatang Anda harus:

  • tambahkan destruktor virtual non-inline bahkan jika badan kosong
  • buat semua konstruktor menjadi non-inline
  • tulis implementasi non-inline dari copy constructor dan operator penugasan kecuali kelas tidak dapat disalin oleh nilai

Ingatlah bahwa inlinekata kunci adalah petunjuk bagi kompiler: kompiler dapat memutuskan untuk tidak menyelaraskan suatu fungsi dan dapat memutuskan untuk menyelaraskan fungsi yang tidak ditandai inlinesejak awal. Saya biasanya menghindari fungsi menandaiinline (selain mungkin ketika menulis fungsi yang sangat kecil).

Tentang kinerja, pendekatan bijak adalah (seperti biasa) untuk membuat profil aplikasi, kemudian pada akhirnya inlineseperangkat fungsi yang mewakili hambatan.

Referensi:


EDIT: Bjarne Stroustrup, Bahasa Pemrograman C ++:

Suatu fungsi dapat didefinisikan sebagai inline. Sebagai contoh:

inline int fac(int n)
{
  return (n < 2) ? 1 : n * fac(n-1);
}

The inlinespecifier adalah petunjuk untuk compiler yang harus berusaha untuk menghasilkan kode untuk panggilan dari fac()inline daripada meletakkan kode untuk fungsi sekali dan kemudian memanggil melalui mekanisme pemanggilan fungsi biasa. Kompiler yang pintar dapat menghasilkan konstanta 720untuk panggilan fac(6). Kemungkinan fungsi inline yang saling rekursif, fungsi inline yang berulang atau tidak bergantung pada input, dll., Membuat tidak mungkin untuk memastikan bahwa setiap panggilan inlinefungsi sebenarnya digarisbawahi. Tingkat kepintaran dari sebuah kompiler tidak dapat diundangkan, sehingga satu kompiler dapat menghasilkan 720, yang lain 6 * fac(5), dan yang lainnya panggilan yang tidak digariskan fac(6).

Untuk memungkinkan inlining jika tidak ada kompilasi yang luar biasa pintar dan fasilitas penghubung, definisi - dan bukan hanya deklarasi - dari fungsi sebaris harus dalam ruang lingkup (§9.2). Sebuah inlineespecifier tidak mempengaruhi semantik fungsi. Secara khusus, fungsi inline masih memiliki alamat unik dan demikian juga staticvariabel (§7.1.2) dari fungsi inline.

EDIT2: ISO-IEC 14882-1998, 7.1.2 Penentu fungsi

Deklarasi fungsi (8.3.5, 9.3, 11.4) dengan inlinespecifier menyatakan fungsi inline. Penspesifikasi sebaris menunjukkan pada implementasi bahwa substitusi sebaris dari fungsi tubuh pada titik panggilan lebih disukai daripada mekanisme panggilan fungsi yang biasa. Implementasi tidak diperlukan untuk melakukan penggantian inline ini di titik panggilan; namun, bahkan jika penggantian inline ini dihilangkan, aturan lain untuk fungsi inline yang didefinisikan oleh 7.1.2 tetap harus dihormati.

Gregory Pakosz
sumber
34
inlinejauh lebih dari sekedar petunjuk bagi kompiler. Ini mengubah aturan bahasa tentang banyak definisi. Juga, memiliki data statis bukanlah alasan untuk menghindari sebaris fungsi. Implementasi wajib mengalokasikan objek statis tunggal untuk setiap fungsi statis terlepas dari apakah fungsi tersebut dinyatakan inlineatau tidak. Kelas masih dapat diperluas jika mereka memiliki konstruktor inline dan destruktor virtual. Dan kosong brace destructor adalah satu fungsi virtual yang kadang - kadang ide yang baik untuk meninggalkan inline.
CB Bailey
2
Ini adalah petunjuk dalam arti fungsi tidak selalu berakhir dengan inline (tetapi bahasa Inggris bukan bahasa ibu saya). Tentang statika dalam fungsi yang ditandai inline, hasilnya adalah bahwa fungsi tersebut tidak mendapatkan inline: Anda membayar harga untuk panggilan tersebut dan juga setiap unit terjemahan yang menyertakan dan memanggil fungsi tersebut mendapatkan salinan kode dan variabel statisnya sendiri. Alasan untuk tidak menggarisbawahi konstruktor dan destruktor ketika mengembangkan perpustakaan adalah kompatibilitas biner dengan versi perpustakaan Anda di masa depan
Gregory Pakosz
14
Tidak akurat untuk menyebutnya "petunjuk untuk kompiler". Pada kenyataannya, non- inlinefungsi dapat digarisbawahi jika kompiler merasa menyukainya. Dan inlinefungsi tidak akan diuraikan jika kompiler memutuskan untuk tidak menyertainya. Seperti kata Charles Bailey, itu mengubah aturan bahasa. Daripada menganggapnya sebagai petunjuk optimisasi, lebih tepat untuk menganggapnya sebagai konsep yang sama sekali berbeda. Kata inlinekunci memberi tahu kompiler untuk memungkinkan beberapa definisi, dan tidak ada yang lain. Optimasi "inlining" dapat diterapkan ke hampir semua fungsi, apakah itu ditandai atau tidak inline.
jalf
26
Hanya saja, ketika Stroustrup menulis "specifier inline adalah petunjuk bagi kompiler", saya terkejut saya disalahkan karena mengutipnya. Bagaimanapun, saya menghabiskan cukup waktu untuk melakukan yang terbaik untuk mendukung jawaban ini dengan referensi sebanyak mungkin
Gregory Pakosz
2
@GregoryPakosz: Tapi kita tidak semua menggunakan inlineuntuk mendapatkan fungsi inlining. Terkadang kita menginginkan manfaat lain, seperti berkeliling ODR.
Lightness Races in Orbit
57

inline tidak ada hubungannya dengan optimasi. inlineadalah instruksi kepada kompiler untuk tidak menghasilkan kesalahan jika fungsi yang diberikan definisi muncul beberapa kali dalam program dan janji bahwa definisi akan muncul di setiap terjemahan yang digunakan dan di mana pun itu muncul akan memiliki definisi yang persis sama.

Mengingat aturan di atas, inlinecocok untuk fungsi pendek yang badannya tidak mengharuskan termasuk dependensi tambahan atas apa yang hanya dibutuhkan deklarasi. Setiap kali definisi ditemukan itu harus diuraikan dan kode untuk tubuhnya dapat dihasilkan sehingga menyiratkan beberapa overhead kompiler atas fungsi yang didefinisikan hanya sekali dalam satu file sumber tunggal.

Kompiler dapat inline (yaitu mengganti panggilan ke fungsi dengan kode yang melakukan tindakan fungsi itu) panggilan fungsi apa pun yang dipilihnya. Dulu kasus itu "jelas" tidak bisa inline fungsi yang tidak dideklarasikan dalam unit terjemahan yang sama dengan panggilan tetapi dengan meningkatnya penggunaan optimasi waktu tautan bahkan ini tidak benar sekarang. Yang sama benarnya adalah fakta bahwa fungsi yang ditandai inlinemungkin tidak diuraikan.

CB Bailey
sumber
Saya merasa bahwa ini lebih merupakan kebetulan yang menyenangkan daripada fitur yang disengaja dari C ++. Idenya sangat mirip dengan variabel global 'statis' dari C. Ini adalah jawaban yang sangat menarik. Saya berharap mereka hanya menggunakan kata kunci seperti 'internal' untuk menunjukkan hubungan internal.
Rehno Lindeque
+1. @Rehno: Saya tidak begitu yakin apa yang Anda katakan. Apa hubungan tautan dengan inlinekata kunci? Dan apa itu kebetulan yang menyenangkan?
jalf
@jalf: Membaca komentar saya dalam retrospeksi, saya menyadari itu agak kabur dan tidak dipikirkan dengan baik. Mendefinisikan fungsi yang sama di banyak file menghasilkan kesalahan linker yang dapat dilawan dengan mendeklarasikan fungsi 'statis'. Namun, 'inline' memungkinkan Anda untuk melakukan hal yang sama dengan perbedaan halus sehingga mereka tidak benar-benar mendapatkan hubungan internal seperti halnya 'statis'. Saya menduga bahwa ini sebenarnya lebih kebetulan karena pelaksana bahasa / desainer menyadari bahwa mereka perlu melakukan sesuatu yang khusus dengan fungsi yang dinyatakan dalam file header dan yang dibawa ke 'inline'.
Rehno Lindeque
4
Tidak yakin mengapa komentar Anda mendapat begitu banyak suara, karena kinerja adalah alasan dominan untuk menggunakan inline.
gast128
10

Memberitahu kompiler untuk menjalankan fungsi adalah optimisasi, dan aturan terpenting optimisasi adalah optimasi prematur adalah akar dari semua kejahatan. Selalu tulis kode yang jelas (menggunakan algoritma yang efisien), lalu buat profil program Anda dan hanya optimalkan fungsi yang terlalu lama.

Jika Anda menemukan fungsi tertentu sangat pendek dan sederhana, dan dipanggil puluhan ribu kali dalam putaran dalam yang ketat, itu mungkin kandidat yang baik.

Namun, Anda mungkin akan terkejut - banyak kompiler C ++ akan secara otomatis menyatukan fungsi-fungsi kecil untuk Anda - dan mereka mungkin mengabaikan permintaan Anda untuk inline juga.

dmazzoni
sumber
Memang, saya memiliki kecurigaan saya bahwa penyusun tertentu sangat diam-diam mengabaikan 'inline' sepenuhnya dan hanya menanggapi '__inline' atau '__force_inline'. Saya kira ini untuk mencegah penyalahgunaan!
Rehno Lindeque
Tidak biasanya demikian. inline hanya sebuah petunjuk, tetapi ini adalah petunjuk yang dianggap serius oleh kebanyakan kompiler. Anda dapat mengatur kompiler untuk memancarkan bahasa rakitan bersama dengan kode objek ( /FAcsdalam Visual Studio, -sdalam GCC) untuk melihat apa yang dilakukannya. Dalam pengalaman saya, kedua kompiler itu menimbang kata kunci sebaris cukup banyak.
Crashworks
1
Sangat menarik, karena dalam pengalaman saya tidak g ++ atau VC menimbang inlinekata kunci sama sekali. Artinya, jika Anda melihat fungsi yang digarisbawahi, dan menghapus inlinespecifier dari itu, itu akan tetap mendapatkan inline. Jika Anda memiliki contoh spesifik sebaliknya, silakan bagikan!
Pavel Minaev
4
bagaimana inlinekata kunci menghalangi "menghapus kode"? Kata kunci dalam "optimasi prematur" adalah prematur , bukan optimasi. Mengatakan bahwa Anda harus aktif * menghindari optimisasi hanyalah sampah. Inti dari kutipan itu adalah Anda harus menghindari optimisasi yang mungkin tidak diperlukan, dan memiliki efek samping yang berbahaya pada kode (seperti membuatnya kurang dapat dipertahankan). Saya gagal melihat bagaimana inlinekata kunci akan membuat kode kurang bisa dipelihara, atau bagaimana bisa berbahaya untuk menambahkannya ke suatu fungsi.
jalf
3
Jalf, kadang-kadang fungsi inlining akan membuat kode Anda lebih lambat, tidak lebih cepat. Salah satu contoh adalah ketika fungsi dipanggil dari beberapa tempat yang berbeda dalam kode Anda; jika fungsi tidak diuraikan, maka itu mungkin masih dalam cache instruksi ketika dipanggil dari tempat yang berbeda, dan prediktor cabang mungkin sudah dihangatkan. Ada beberapa pola yang selalu meningkatkan efisiensi, jadi tidak ada salahnya untuk menggunakannya. Inlining bukan salah satunya. Biasanya tidak berpengaruh pada kinerja sama sekali, kadang membantu, dan kadang menyakitkan. Saya berdiri di belakang saran saya: profil dulu, lalu sebaris.
dmazzoni
5

Cara terbaik untuk mengetahuinya adalah dengan membuat profil program Anda dan menandai fungsi-fungsi kecil yang dipanggil berkali-kali dan membakar melalui siklus CPU yang inline . Kata kunci di sini adalah "kecil" - setelah overhead panggilan fungsi diabaikan dibandingkan dengan waktu yang dihabiskan dalam fungsi, tidak ada gunanya untuk menyejajarkannya.

Penggunaan lain yang saya sarankan adalah jika Anda memiliki fungsi-fungsi kecil yang cukup sering dipanggil dalam kode kinerja kritis untuk membuat cache miss relevan, Anda mungkin harus menyejajarkannya juga. Sekali lagi, itu adalah sesuatu yang harus bisa disampaikan oleh profiler.

Timo Geusch
sumber
4

Optimalisasi prematur adalah akar dari semua kejahatan!

Sebagai aturan praktis saya biasanya hanya sebaris "getter" dan "setters". Setelah kode berfungsi dan stabil, pembuatan profil dapat menunjukkan fungsi mana yang bisa mendapat manfaat dari inlining.

Di sisi lain, sebagian besar kompiler modern memiliki algoritma optimisasi yang cukup baik, dan akan menguraikan apa yang seharusnya Anda sebariskan untuk Anda.

Reasuming - menulis fungsi one-liner sebaris, dan khawatir tentang orang lain nanti.

Kornel Kisielewicz
sumber
2

Fungsi sebaris mungkin meningkatkan kinerja kode Anda dengan menghilangkan kebutuhan untuk mendorong argumen ke dalam tumpukan. jika fungsi yang dimaksud adalah bagian penting dari kode Anda, Anda harus membuat keputusan inline bukan inline di bagian optimisasi proyek Anda,

Anda dapat membaca lebih lanjut tentang inline di c ++ faq

Alon
sumber
1

Saya sering menggunakan fungsi sebaris bukan sebagai optimasi tetapi untuk membuat kode lebih mudah dibaca. Terkadang kode itu sendiri lebih pendek dan lebih mudah dipahami daripada komentar, nama deskriptif dll. Misalnya:

void IncreaseCount() { freeInstancesCnt++; }

Pembaca segera mengetahui semantik kode yang lengkap.

danatel
sumber
0

Saya biasanya mengikuti aturan praktis di mana saya membuat fungsi dengan 3-4 pernyataan sederhana sebagai inline. Tetapi baik untuk diingat bahwa itu hanyalah petunjuk bagi kompiler. Panggilan terakhir untuk membuatnya sejajar atau tidak hanya diambil oleh kompiler. Jika ada lebih dari banyak pernyataan ini saya tidak akan mendeklarasikan sebaris dengan kompiler bodoh itu dapat menyebabkan kode mengasapi.

Naveen
sumber
0

Cara terbaik adalah dengan memeriksa dan membandingkan instruksi yang dihasilkan untuk inline dan tidak inline. Namun, selalu aman untuk dihilangkan inline. Menggunakan inlinedapat menyebabkan masalah yang tidak Anda inginkan.

wallyk
sumber
0

Ketika memutuskan apakah akan menggunakan inline, saya biasanya mengingat ide berikut: Pada komputer modern, latensi memori dapat menjadi penghambat yang lebih besar daripada perhitungan mentah. Fungsi inlining yang disebut sering diketahui menumbuhkan ukuran yang dapat dieksekusi. Lebih jauh, fungsi seperti itu dapat disimpan dalam cache kode CPU yang akan mengurangi jumlah cache yang hilang ketika kode itu perlu diakses.

Oleh karena itu, Anda harus memutuskan sendiri: Apakah menambahkan atau mengurangi ukuran kode mesin yang dihasilkan? Seberapa besar kemungkinan bahwa memanggil fungsi akan menyebabkan cache hilang? Jika dibumbui di seluruh kode, maka saya akan mengatakan kemungkinannya tinggi. Jika dibatasi untuk satu loop ketat maka kemungkinan mudah-mudahan rendah.

Saya biasanya menggunakan inlining dalam daftar kasus saya di bawah ini. Namun, di mana Anda benar-benar peduli dengan kinerja, membuat profil sangat penting. Selanjutnya, Anda mungkin ingin memeriksa apakah kompiler benar-benar menerima petunjuk.

  • Rutinitas singkat yang disebut dalam loop ketat.
  • Accessor yang sangat mendasar (get / set) dan fungsi wrapper.
  • Kode template dalam file header sayangnya secara otomatis mendapatkan petunjuk inline.
  • Kode pendek yang digunakan seperti makro. (Misalnya, min () / maks ())
  • Rutin matematika pendek.
Rehno Lindeque
sumber
0

Juga, metode inline memiliki efek samping yang parah ketika mempertahankan proyek besar. Ketika kode inline diubah, semua file yang menggunakannya akan dibangun kembali secara otomatis oleh kompiler (itu adalah kompiler yang baik). Ini bisa menghabiskan banyak waktu pengembangan Anda.

Ketika suatu inlinemetode ditransfer ke file sumber dan tidak diuraikan lagi, seluruh proyek harus dibangun kembali (setidaknya ini adalah pengalaman saya). Dan juga ketika metode dikonversi menjadi inline.

Thomas Matthews
sumber
1
Itu masalah yang berbeda. Anda mendapatkan masalah rekondisi untuk kode yang ditempatkan di file header. Apakah itu ditandai inlineatau tidak tidak masalah (selain tanpa inlinekata kunci, Anda akan mendapatkan error linker - tetapi inlinekata kunci bukanlah masalah yang menyebabkan membangun kembali berlebihan.
jalf
Namun, mengubah metode inline akan menyebabkan build berlebihan versus mengubah metode non-inline dalam file shource.
Thomas Matthews
0

Seseorang harus menggunakan kualifikasi fungsi sebaris hanya ketika kode fungsi kecil. Jika fungsinya lebih besar Anda harus memilih fungsi normal karena menghemat ruang memori sebanding dengan pengorbanan yang relatif kecil dalam kecepatan eksekusi.

nitish
sumber
0

Ketika Anda berpikir kode Anda cukup kecil untuk digunakan sebagai inline dan ingatlah fungsi inline menduplikat kode Anda dan menempelkannya ketika fungsi dipanggil sehingga mungkin cukup baik untuk meningkatkan waktu eksekusi Anda tetapi juga meningkatkan konsumsi memori. Anda tidak dapat menggunakan fungsi inline ketika Anda menggunakan fungsi loop / variabel statis / rekursif / switch / goto / Virtual. Virtual berarti menunggu hingga runtime dan inline berarti selama kompilasi sehingga mereka tidak dapat digunakan secara bersamaan.

sachin pathak
sumber
-2

Saya telah membaca beberapa jawaban dan melihat ada beberapa hal yang hilang.

Aturan yang saya gunakan adalah tidak menggunakan inline, kecuali saya ingin inline. Tampak konyol, sekarang penjelasan.

Kompiler cukup pintar dan fungsi pendek selalu sejajar. Dan tidak pernah menjadikan fungsi panjang sebagai inline, kecuali jika programmer mengatakan untuk melakukan itu.

Saya tahu bahwa inline adalah petunjuk atau permintaan untuk kompiler

Sebenarnya inlineini adalah pesanan untuk kompiler, ia tidak punya pilihan dan setelah inlinekata kunci membuat semua kode sebaris. Jadi Anda tidak pernah bisa menggunakan inlinekata kunci dan kompiler akan mendesain kode terpendek.

Jadi kapan harus digunakan inline?

Untuk digunakan jika Anda ingin memiliki beberapa kode sebaris. Saya hanya tahu satu contoh, karena saya menggunakannya hanya dalam satu situasi. Ini adalah otentikasi pengguna.

Misalnya saya punya fungsi ini:

inline bool ValidUser(const std::string& username, const std::string& password)
{
    //here it is quite long function
}

Tidak peduli seberapa besar fungsi ini saya ingin memilikinya sebagai inline karena itu membuat perangkat lunak saya lebih sulit untuk di-crack.

ST3
sumber
2
inline masih merupakan petunjuk. Compiler dapat gagal untuk inline jika dianggap bahwa fungsi Anda terlalu membengkak.
It'sPete
Yang satu mengatakan inline adalah perintah ... yang lain mengatakan itu petunjuk. Akankah seseorang mendukung pernyataannya sehingga kita dapat menentukan mana yang benar?
@ user2918461 saya mendukung pernyataan inline hanyalah sebuah petunjuk. Ini telah didukung oleh banyak situs web dan buku
WARhead