Kapan saya harus menulis kata kunci inline
untuk fungsi / metode dalam C ++?
Setelah melihat beberapa jawaban, beberapa pertanyaan terkait:
Kapan saya tidak boleh menulis kata kunci 'inline' untuk fungsi / metode dalam C ++?
Kapan kompiler tidak tahu kapan membuat fungsi / metode 'inline'?
Apakah masalah jika aplikasi multithreaded ketika seseorang menulis 'inline' untuk suatu fungsi / metode?
c++
inline
one-definition-rule
Sebagian
sumber
sumber
inline
(9.3 / 2).Jawaban:
Oh man, salah satu kencing kesayangan saya.
inline
lebih sepertistatic
atauextern
dari pada arahan yang memberitahu kompiler untuk menampilkan fungsi Anda.extern
,static
,inline
Adalah arahan linkage, digunakan hampir secara eksklusif oleh linker, bukan compiler.Dikatakan bahwa
inline
mengisyaratkan kepada kompiler bahwa Anda pikir fungsi tersebut harus diuraikan. Itu mungkin benar pada tahun 1998, tetapi satu dekade kemudian kompiler tidak membutuhkan petunjuk seperti itu. Belum lagi manusia biasanya salah dalam hal mengoptimalkan kode, sehingga sebagian besar penyusun mengabaikan abaikan 'petunjuk'.static
- nama variabel / fungsi tidak dapat digunakan di unit terjemahan lainnya. Linker perlu memastikan bahwa itu tidak sengaja menggunakan variabel / fungsi yang ditentukan secara statis dari unit terjemahan lain.extern
- gunakan nama variabel / fungsi ini dalam unit terjemahan ini tetapi jangan mengeluh jika itu tidak didefinisikan. Linker akan mengatasinya dan memastikan semua kode yang mencoba menggunakan simbol eksternal memiliki alamatnya.inline
- fungsi ini akan didefinisikan dalam beberapa unit terjemahan, jangan khawatir tentang hal itu. Linker perlu memastikan semua unit terjemahan menggunakan instance variabel / fungsi tunggal.Catatan: Secara umum, menyatakan templat
inline
tidak ada gunanya, karena sudah memiliki semantik tautaninline
. Namun, spesialisasi eksplisit dan instantiasi templat perluinline
digunakan.Jawaban khusus untuk pertanyaan Anda:
Hanya ketika Anda ingin fungsi tersebut didefinisikan di header. Lebih tepatnya hanya ketika definisi fungsi dapat muncul dalam beberapa unit terjemahan. Ini adalah ide yang baik untuk mendefinisikan fungsi-fungsi kecil (seperti dalam satu liner) dalam file header karena memberikan kompiler lebih banyak informasi untuk bekerja sambil mengoptimalkan kode Anda. Ini juga meningkatkan waktu kompilasi.
Jangan tambahkan inline hanya karena Anda pikir kode Anda akan berjalan lebih cepat jika kompiler menyatukannya.
Secara umum, kompiler akan dapat melakukan ini lebih baik daripada Anda. Namun, kompiler tidak memiliki opsi untuk kode inline jika tidak memiliki definisi fungsi. Dalam kode optimal yang dioptimalkan biasanya semua
private
metode diuraikan apakah Anda memintanya atau tidak.Sebagai tambahan untuk mencegah penyatuan dalam GCC, gunakan
__attribute__(( noinline ))
, dan di Visual Studio, gunakan__declspec(noinline)
.Multithreading tidak memengaruhi inlining dengan cara apa pun.
sumber
inline
kata kunci tidak terkait. Anda punya ide yang tepat. Sebagai aturan, menebak apa yang akan diperbaiki dengan inlining sangat rentan kesalahan. Pengecualian untuk aturan itu menjadi satu baris.Saya ingin berkontribusi pada semua jawaban hebat di utas ini dengan contoh yang meyakinkan untuk membubarkan kesalahpahaman yang tersisa.
Diberikan dua file sumber, seperti:
inline111.cpp:
inline222.cpp:
Kasus A:
Kompilasi :
Keluaran :
Diskusi :
Walaupun Anda harus memiliki definisi identik fungsi inline Anda, kompiler C ++ tidak menandainya jika bukan itu masalahnya (sebenarnya, karena kompilasi terpisah ia tidak memiliki cara untuk memeriksanya). Adalah tugas Anda sendiri untuk memastikan ini!
Linker tidak mengeluh tentang Aturan Satu Definisi , sebagaimana
fun()
dinyatakan sebagaiinline
. Namun, karena inline111.cpp adalah unit terjemahan pertama (yang sebenarnya memanggilfun()
) yang diproses oleh kompiler, kompiler instantiatefun()
setelah pertemuan-panggilan pertamanya di inline111.cpp . Jika kompiler memutuskan untuk tidak memperluasfun()
panggilannya dari tempat lain di program Anda ( mis. Dari inline222.cpp ), panggilan kefun()
akan selalu ditautkan ke turunannya yang dihasilkan dari inline111.cpp (panggilan kefun()
dalam inline222.cpp)mungkin juga menghasilkan sebuah instance di unit terjemahan itu, tetapi itu akan tetap tidak terhubung). Memang, itu terbukti dari hasil&fun = 0x4029a0
cetakan yang identik .Akhirnya, terlepas dari
inline
saran kepada kompiler untuk benar - benar memperluas satu-linerfun()
, ia mengabaikan saran Anda sepenuhnya, yang jelas karenafun() = 111
di kedua baris.Kasus B:
Kompilasi (perhatikan urutan terbalik) :
Keluaran :
Diskusi :
Kasus ini menegaskan apa yang telah dibahas dalam Kasus A .
Perhatikan poin penting, bahwa jika Anda komentar panggilan sebenarnya untuk
fun()
di inline222.cpp ( misalnya komentar keluarcout
pernyataan-di inline222.cpp benar-benar) kemudian, meskipun urutan kompilasi unit terjemahan Anda,fun()
akan dipakai pada itu pertemuan pertama panggilan di inline111.cpp , menghasilkan print-out untuk Kasus B sebagaiinline111: fun() = 111, &fun = 0x402980
.Kasus C:
Kompilasi (pemberitahuan -O2) :
atau
Keluaran :
Diskusi :
-O2
optimasi mendorong compiler untuk benar-benar memperluas fungsi yang dapat inline (Perhatikan juga bahwa-fno-inline
adalah standar tanpa opsi optimasi). Seperti terbukti dari cetakan di sini,fun()
sebenarnya telah diperluas sebaris (sesuai dengan definisi dalam unit terjemahan tertentu ), menghasilkan dua cetakan yang berbedafun()
. Meskipun demikian, hanya ada satu contoh terkait globalfun()
(seperti yang dipersyaratkan oleh standar), seperti yang terbukti dari hasil cetak yang identik&fun
.sumber
inline
fungsi seperti itu menjadi perilaku yang tidak terdefinisi..cpp
- masing menjadi unit terjemahannya sendiri. Lebih disukai, tambahkan case untuk-flto
diaktifkan / dinonaktifkan.Anda masih perlu secara jelas menyejajarkan fungsi Anda saat melakukan spesialisasi template (jika spesialisasi ada dalam file .h)
sumber
1) Saat ini, hampir tidak pernah. Jika itu adalah ide bagus untuk sebaris fungsi, kompiler akan melakukannya tanpa bantuan Anda.
2) Selalu. Lihat # 1.
(Diedit untuk mencerminkan bahwa Anda membagi pertanyaan Anda menjadi dua pertanyaan ...)
sumber
inline
masih diperlukan, misalnya untuk mendefinisikan fungsi dalam file header (dan itu diperlukan untuk menguraikan fungsi tersebut di beberapa unit kompilasi).inline
specifier, instansnya secara otomatis diciutkan menjadi satu oleh linker, dan ODR tidak digunakan.Jika fungsi tersebut dideklarasikan di header dan didefinisikan dalam
.cpp
file, Anda tidak boleh menulis kata kunci.Tidak ada situasi seperti itu. Kompiler tidak dapat membuat fungsi sebaris. Yang bisa dilakukan hanyalah menyisipkan beberapa atau semua panggilan ke fungsi. Ia tidak dapat melakukannya jika belum memiliki kode fungsi (dalam hal ini penghubung perlu melakukannya jika ia mampu melakukannya).
Tidak, itu tidak masalah sama sekali.
sumber
Ini tergantung pada kompiler yang digunakan. Jangan percaya secara membabi buta bahwa kompiler saat ini lebih tahu daripada manusia cara inline dan Anda tidak boleh menggunakannya untuk alasan kinerja, karena itu adalah arahan tautan daripada petunjuk pengoptimalan. Sementara saya setuju bahwa secara ideologis argumen-argumen ini benar menghadapi kenyataan mungkin merupakan hal yang berbeda.
Setelah membaca beberapa utas di sekitar saya mencoba rasa ingin tahu efek inline pada kode saya hanya bekerja dan hasilnya adalah saya mendapat speedup terukur untuk GCC dan tidak ada kecepatan untuk kompiler Intel.
(Lebih detail: simulasi matematika dengan beberapa fungsi kritis yang didefinisikan di luar kelas, GCC 4.6.3 (g ++ -O3), ICC 13.1.0 (icpc -O3); menambahkan sebaris ke titik-titik kritis yang menyebabkan peningkatan + 6% dengan kode GCC).
Jadi, jika Anda memenuhi syarat GCC 4.6 sebagai kompiler modern, hasilnya adalah arahan inline masih penting jika Anda menulis tugas intensif CPU dan tahu di mana sebenarnya hambatannya.
sumber
Pada kenyataannya, hampir tidak pernah. Yang Anda lakukan hanyalah menyarankan agar kompiler membuat fungsi yang diberikan sebaris (mis., Ganti semua panggilan ke fungsi ini / di badannya). Tidak ada jaminan, tentu saja: kompiler dapat mengabaikan arahan.
Kompiler umumnya akan melakukan pekerjaan yang baik untuk mendeteksi + mengoptimalkan hal-hal seperti ini.
sumber
inline
memiliki perbedaan semantik dalam C ++ (misalnya dalam cara beberapa definisi diperlakukan), yang penting dalam beberapa kasus (misalnya templat).Saya memeriksa ini untuk Visual Studio 9 (15.00.30729.01) dengan mengkompilasi dengan / FAcs dan melihat kode perakitan: Kompiler menghasilkan panggilan ke fungsi anggota tanpa optimasi diaktifkan di mode debug . Bahkan jika fungsi ditandai dengan __forceinline , tidak ada kode runtime inline yang diproduksi.
sumber
Anda ingin memasukkannya di awal, sebelum kembali ketik. Tetapi kebanyakan Compiler mengabaikannya. Jika sudah ditentukan, dan memiliki blok kode yang lebih kecil, sebagian besar kompiler menganggapnya sebaris.
sumber
Kecuali jika Anda menulis perpustakaan atau memiliki alasan khusus, Anda dapat melupakan
inline
dan menggunakan pengoptimalan waktu tautan . Ini menghapus persyaratan bahwa definisi fungsi harus di header untuk itu dipertimbangkan untuk digariskan di seluruh unit kompilasi, yang persis apa yanginline
memungkinkan.(Tapi lihat apakah ada alasan mengapa tidak menggunakan optimasi waktu tautan? )
sumber
Kata kunci sebaris meminta kompiler untuk mengganti panggilan fungsi dengan badan fungsi, ia pertama-tama mengevaluasi ekspresi dan kemudian berlalu. Ini mengurangi overhead panggilan fungsi karena tidak perlu menyimpan alamat pengirim dan memori tumpukan tidak diperlukan untuk fungsi argumen.
Kapan harus menggunakan:
sumber
inline
atau tidak di C dan C ++. C Inline: stackoverflow.com/a/62287072/7194773 C ++ inline: stackoverflow.com/a/62230963/7194773C ++ inline benar-benar berbeda dengan C inline .
inline
sendiri mempengaruhi compiler, assembler dan linker. Ini adalah arahan ke kompiler yang mengatakan hanya memancarkan simbol untuk fungsi / data ini jika digunakan dalam unit terjemahan, dan jika ya, maka seperti metode kelas, beri tahu assembler untuk menyimpannya di bagian.section .text.c::function(),"axG",@progbits,c::function(),comdat
atau.section .bss.i,"awG",@nobits,i,comdat
untuk data. Instansiasi template juga masuk dalam grup comdat mereka sendiri.Berikut ini
.section name, "flags"MG, @type, entsize, GroupName[, linkage]
. Misalnya, nama bagiannya adalah.text.c::function()
.axG
berarti bagian tersebut dapat dialokasikan, dapat dieksekusi, dan dalam sebuah grup yaitu nama grup akan ditentukan (dan tidak ada bendera M sehingga tidak ada entsize yang akan ditentukan);@progbits
berarti bagian tersebut berisi data dan tidak kosong;c::function()
adalah nama grup dan grup tersebutcomdat
keterkaitan artinya dalam semua file objek, semua bagian yang ditemukan dengan nama grup ini yang ditandai dengan comdat akan dihapus dari executable final kecuali untuk 1 yaitu kompiler memastikan bahwa hanya ada satu definisi dalam unit terjemahan dan kemudian memberitahu assembler untuk menempatkan itu dalam grup sendiri di file objek (1 bagian dalam 1 grup) dan kemudian linker akan memastikan bahwa jika ada file objek memiliki grup dengan nama yang sama, maka hanya memasukkan satu di final .exe. Perbedaan antarainline
dan tidak menggunakaninline
sekarang terlihat oleh assembler dan akibatnya linker, karena itu tidak disimpan dalam.data
atau biasa.text
dll oleh assembler karena arahan mereka.static inline
dalam sebuah kelas berarti ini merupakan definisi tipe dan bukan deklarasi (memungkinkan anggota statis untuk didefinisikan dalam kelas) dan membuatnya inline; sekarang berperilaku seperti di atas.static inline
pada lingkup file hanya memengaruhi kompiler. Ini berarti ke kompiler: hanya memancarkan simbol untuk fungsi / data ini jika digunakan dalam unit terjemahan dan melakukannya sebagai simbol statis biasa (menyimpan in.text /.data tanpa direktif .globl). Untuk assembler sekarang tidak ada perbedaan antarastatic
danstatic inline
extern inline
adalah deklarasi yang berarti Anda harus mendefinisikan simbol ini di unit terjemahan atau melempar kesalahan kompiler; jika sudah didefinisikan maka perlakukan sebagai regulerinline
dan untuk assembler dan linker tidak akan ada perbedaan antaraextern inline
daninline
, jadi ini hanya penjaga kompiler.Keseluruhan di atas tanpa garis kesalahan runtuh menjadi
inline int i[5]
. Jelas jika Anda melakukanextern inline int i[] = {5};
ituextern
akan diabaikan karena definisi eksplisit melalui penugasan.inline
di namespace, lihat ini dan inisumber
Saat mengembangkan dan men-debug kode, tinggalkan
inline
. Ini menyulitkan debugging.Alasan utama untuk menambahkannya adalah untuk membantu mengoptimalkan kode yang dihasilkan. Biasanya ini memperdagangkan ruang kode yang meningkat untuk kecepatan, tetapi terkadang
inline
menghemat ruang kode dan waktu eksekusi.Mengeluarkan pemikiran seperti ini tentang optimasi kinerja sebelum penyelesaian algoritma adalah optimasi prematur .
sumber
inline
fungsi biasanya tidak digarisbawahi kecuali dikompilasi dengan optimisasi, sehingga tidak mempengaruhi debugging dengan cara apa pun. Ingat bahwa itu adalah petunjuk, bukan permintaan.inline
fungsinya diselaraskan. Tidak mungkin untuk menetapkan breakpoint yang berarti di dalamnya.inline
tidak akan melakukan apa pun untuk meningkatkan kode pada kompiler modern, yang dapat menentukan apakah akan inline atau tidak dengan sendirinya.Kapan seseorang harus sebaris:
1. Ketika seseorang ingin menghindari overhead dari hal-hal yang terjadi ketika fungsi disebut seperti parameter passing, transfer kontrol, kontrol kembali dll.
2.Fungsi harus kecil, sering dipanggil dan membuat inline benar-benar menguntungkan karena sesuai aturan 80-20, cobalah untuk membuat fungsi inline yang memiliki dampak besar pada kinerja program.
Seperti yang kita ketahui bahwa inline hanyalah permintaan untuk mengkompilasi mirip dengan mendaftar dan itu akan dikenakan biaya pada ukuran kode objek.
sumber
inline
telah kehilangan statusnya sebagai petunjuk optimasi, dan kebanyakan kompiler hanya menggunakannya untuk membuat kelonggaran untuk beberapa definisi - sebagaimana IMO seharusnya. Lebih dari itu, sejak C ++ 11,register
telah sepenuhnya ditinggalkan karena makna sebelumnya dari 'Saya tahu lebih baik daripada kompiler bagaimana mengoptimalkan': itu sekarang hanya kata yang dipesan tanpa makna saat ini.inline
taraf tertentu.Fungsi C ++ inline adalah konsep yang kuat yang biasa digunakan dengan kelas. Jika suatu fungsi inline, kompiler menempatkan salinan kode dari fungsi itu di setiap titik di mana fungsi dipanggil pada waktu kompilasi.
Setiap perubahan ke fungsi sebaris dapat meminta semua klien dari fungsi tersebut untuk dikompilasi ulang karena kompiler harus mengganti semua kode sekali lagi jika tidak akan dilanjutkan dengan fungsi lama.
Untuk inline suatu fungsi, tempatkan inline kata kunci di depan nama fungsi dan tentukan fungsi sebelum panggilan dilakukan ke fungsi. Compiler dapat mengabaikan kualifikasi inline jika fungsi yang didefinisikan lebih dari satu baris.
Definisi fungsi dalam definisi kelas adalah definisi fungsi inline, bahkan tanpa menggunakan inline specifier.
Berikut ini adalah contoh, yang menggunakan fungsi inline untuk mengembalikan maksimal dua angka
untuk informasi lebih lanjut lihat di sini .
sumber