Menurut apa yang saya baca, kompiler tidak berkewajiban untuk mengganti fungsi panggilan fungsi inline dengan tubuhnya, tetapi akan melakukannya jika bisa. Ini membuat saya berpikir - mengapa kita memiliki kata inline jika itu masalahnya? Mengapa tidak membuat semua fungsi sebaris fungsi secara default dan membiarkan kompiler mengetahui apakah ia dapat menggantikan panggilan dengan badan fungsi atau tidak?
c++
language-design
optimization
compilation
EpsilonVector
sumber
sumber
Jawaban:
inline
berasal dari C; itu bukan hal baru untuk C ++.Ada kata kunci C (
register
daninline
) yang dirancang untuk memungkinkan programmer untuk membantu dalam optimasi kode. Ini umumnya diabaikan saat ini, karena kompiler dapat melakukan yang lebih baik dalam mendaftar tugas dan memutuskan kapan harus inline fungsi (pada kenyataannya, kompiler dapat inline atau tidak inline fungsi pada waktu yang berbeda). Pembuatan kode pada prosesor modern jauh lebih rumit daripada pada yang lebih deterministik umum ketika Ritchie menciptakan C.Apa arti kata itu sekarang, dalam C ++, adalah bahwa ia dapat memiliki banyak definisi identik, dan perlu didefinisikan di setiap unit terjemahan yang menggunakannya. (Dengan kata lain, Anda perlu memastikan itu dapat digarisbawahi.) Anda dapat memiliki
inline
fungsi di header tanpa masalah, dan fungsi anggota yang didefinisikan dalam definisi kelas secara otomatis efektifinline
.sumber
inline
.inline
itu standar di C ++ pertama, meskipun sudah tersedia sebagai ekstensi vendor di C .... ya, sepertinya itu ditambahkan ke standar C di C99.inline
di C99 dan yang lebih baru berbeda dengan aturaninline
di dalam C ++.Awalnya
inline
adalah petunjuk yang sangat kuat bahwa panggilan ke fungsi harus diuraikan.Tetapi satu-satunya efek dijamin
inline
adalah untuk memungkinkan suatu fungsi untuk didefinisikan (secara efektif identik) dalam beberapa unit terjemahan, misalnya, bahwa Anda menempatkan definisi dalam file header.Saat ini, beberapa kompiler sangat tertarik mengikuti petunjuk inlining, misalnya g ++. Dan beberapa kompiler menganggapnya kurang serius, misalnya Visual C ++. Tetapi semua harus mematuhi jaminan.
Sangat disayangkan bahwa kedua makna ini - petunjuk pengoptimalan dan apa yang kita sebut definisi dibuang tingkat tautan - berada dengan kata kunci yang sama, karena itu berarti bahwa Anda tidak dapat secara praktis memilikinya tanpa yang lain.
Sangat disayangkan bahwa
inline
(atau lebih baik, kata kunci terpisah tentang definisi yang dapat dibuang) ¹tidak dapat diterapkan pada data .Kebutuhan akan data yang dapat dibuang pada level linker telah meningkat karena modul yang hanya menggunakan header menjadi lebih populer. Misalnya, banyak sub-pustaka Boost hanya header-saja.
Namun, untuk data Anda dapat menerapkan sedikit trik dengan templat. Tentukan dalam beberapa templat kelas, berikan
typedef
parameter templat denganvoid
(atau apa pun). Itu karena Aturan Satu Definisi membuat pengecualian khusus untuk template.Catatan: variabel
¹
inline
akan didukung dalam C ++ 17 .sumber
inline
.Mengapa tidak membuat semua fungsi sebaris secara default? Karena ini merupakan trade off teknik. Setidaknya ada dua jenis "optimasi": mempercepat program dan mengurangi ukuran (jejak memori) program. Inlining umumnya mempercepat. Ini menghilangkan overhead panggilan fungsi, menghindari mendorong lalu menarik parameter dari stack. Namun, itu juga membuat tapak memori program lebih besar, karena setiap pemanggilan fungsi sekarang harus diganti dengan kode lengkap fungsi. Untuk membuat segala sesuatunya menjadi lebih rumit, ingatlah bahwa toko CPU sering menggunakan potongan memori dalam cache pada CPU untuk akses ultra cepat. Jika Anda membuat gambar memori program cukup besar, program Anda tidak akan dapat menggunakan cache secara efisien, dan dalam kasus terburuk inlining sebenarnya dapat memperlambat program Anda.
sumber
Untuk memahami “sebaris” Anda perlu memahami sejarah dan seperti apa kehidupan 20 (dan 30) tahun yang lalu.
Kami sedang menulis kode pada komputer yang memiliki sedikit memori, jadi tidak mungkin bagi kompiler untuk memproses semua kode yang membentuk sebuah program sekaligus. Kompiler juga sangat lambat, jadi Anda tidak ingin harus mengkompilasi ulang kode yang tidak berubah - mengambil alih 24 jam (pada komputer yang harganya lebih dari mobil kelas atas) untuk mengkompilasi ulang semua kode yang normal untuk beberapa proyek I bekerja pada.
Oleh karena itu setiap file kode dikompilasi secara terpisah menjadi file objek. Setiap file objek dimulai dengan daftar semua fungsi yang dikandungnya, bersama dengan "alamat" fungsi. File objek juga memiliki daftar semua fungsi yang dipanggil dalam file objek lain bersama dengan lokasi panggilan.
Seorang linker pertama-tama akan membaca semua file objek, dan menyusun daftar semua fungsi yang mereka ekspor, bersama dengan file yang ada di sana dan alamatnya. Kemudian akan membaca kembali semua file objek, mengeluarkannya ke file program, sambil memperbarui semua panggilan fungsi "eksternal" dengan alamat fungsi.
Linker tidak mengubah atau mengoptimalkan kode mesin yang dihasilkan oleh kompiler dengan cara apa pun selain untuk memperbaiki referensi untuk panggilan fungsi eksternal. Linker adalah bagian dari sistem operasi dan mendahului sebagian besar kompiler. Ketika orang menulis kompiler baru, mereka memerlukannya untuk bekerja dengan penghubung saat ini, dan untuk dapat menautkan ke file objek saat ini, jika tidak, panggilan sistem tidak dapat dibuat.
Kompiler hanya pernah melihat kode dalam file ".c" atau ".cpp" yang dikompilasi bersama dengan semua file header yang disertakan. Jadi itu tidak dapat membuat optimasi berdasarkan kode di file ".c" atau ".cpp" lainnya.
Kata kunci "inline" memungkinkan tubuh fungsi (metode) untuk didefinisikan dalam file header, sehingga memungkinkan kompiler untuk menggunakan kode fungsi sambil mengkompilasi kode yang memanggilnya. Misalnya, Anda memiliki kelas koleksi yang ditentukan dalam file .cpp anther, kelas ini akan memiliki metode "isEmpty", yang berisi satu baris kode, akan ada percepatan besar dari program yang dihasilkan jika alih-alih panggilan ke fungsi , panggilan fungsi diganti dengan satu baris ini.
Kata kunci "inline" dilihat pada saat itu sebagai cara "murah dan mudah" untuk memungkinkan enkapsulasi data sambil menghindari biaya pemanggilan fungsi, tanpa itu banyak programmer akan hanya mengakses bidang pribadi objek. (Makro di mana cara yang jauh lebih buruk "inlining" kode yang mana umum pada saat itu.)
“Linker” akhir-akhir ini melakukan banyak optimasi kode dan cenderung ditulis oleh beberapa tim sebagai kompiler. Compiler sering hanya memeriksa kode yang benar dan "kompres" itu, meninggalkan sebagian besar tugas pembuatan kode mesin ke linker.
sumber
Mari kita lihat apa yang dikatakan standar (bagian penting yang dicetak tebal):
Jadi, jika Anda ingin memastikan, Anda harus membaca dokumentasi kompiler Anda.
Memasukkan semuanya adalah ide yang buruk, karena mungkin menghasilkan banyak kode mesin duplikat ...
Jadi, Anda harus tahu:
sumber
inline
dengan mengulangi pengetahuan sebagai OP tampaknya melewatkan bagian jadi yang merupakan alasan utama mengapa dia tidak mendapatkan intinya. Agar seseorang mendapatkan poin, dia perlu memiliki pemahaman tentang apa yang ada di bawah poin itu ...Biarkan saya memberi Anda alasan bagus untuk menggunakan kata kunci inline.
Pada sistem yang disematkan, seperti printer tiket atau sistem kecil yang serupa. Prosesor sangat terbatas dan pemanggilan fungsi (menyiapkan parameter fungsi pada stack, panggilan, mengambil params dari stack, dan mengembalikan jawaban, dll.) Dapat mengambil beberapa ms untuk dijalankan, di samping fungsi itu sendiri.
Katakanlah waktu panggilan adalah sekitar 60 ms (hanya untuk menelepon, bukan fungsi sebenarnya) dan Anda harus melakukan 50 iterasi (loop atau panggilan berulang di pohon).
Waktu untuk hanya bergerak maju dan mundur dari pemanggilan fungsi itu akan membutuhkan 60 * 50 = 3000 (3 detik).
Jika Anda memiliki memori, Anda pasti akan melakukan inline untuk menghemat 3 detik.
Jadi inline pada dasarnya digunakan ketika Anda membutuhkan kecepatan eksekusi. Dalam beberapa proyek saya terlibat, waktu panggilan lebih lama dari waktu eksekusi, situasi klasik kapan harus menggunakan inline.
sumber