Adakah alasan kinerja yang meyakinkan untuk memilih tautan statis daripada tautan dinamis atau sebaliknya dalam situasi tertentu? Saya pernah mendengar atau membaca yang berikut, tetapi saya tidak cukup tahu tentang subjek untuk menjamin kebenarannya.
1) Perbedaan kinerja runtime antara tautan statis dan tautan dinamis biasanya dapat diabaikan.
2) (1) tidak benar jika menggunakan kompiler profiling yang menggunakan data profil untuk mengoptimalkan hotpath program karena dengan tautan statis, kompiler dapat mengoptimalkan kode Anda dan kode perpustakaan. Dengan tautan dinamis, hanya kode Anda yang dapat dioptimalkan. Jika sebagian besar waktu dihabiskan untuk menjalankan kode perpustakaan, ini dapat membuat perbedaan besar. Kalau tidak, (1) masih berlaku.
Jawaban:
Beberapa suntingan menyertakan saran yang sangat relevan dalam komentar dan jawaban lainnya. Saya ingin mencatat bahwa cara Anda memecahkan masalah ini sangat tergantung pada lingkungan apa yang Anda rencanakan untuk dijalankan. Sistem seminimal mungkin tidak memiliki sumber daya yang cukup untuk mendukung tautan dinamis. Sistem kecil yang sedikit lebih besar mungkin mendukung tautan dinamis, karena memori mereka cukup kecil untuk membuat penghematan RAM dari tautan dinamis sangat menarik. PC konsumen penuh sesak, seperti yang dicatat Mark, sumber daya yang sangat besar, dan Anda mungkin dapat membiarkan masalah kenyamanan mendorong pemikiran Anda tentang masalah ini.
Untuk mengatasi masalah kinerja dan efisiensi: itu tergantung .
Secara klasik, pustaka dinamis memerlukan semacam lapisan lem yang sering berarti pengiriman ganda atau lapisan tanpa arah tambahan dalam pengalamatan fungsi dan dapat memakan biaya sedikit kecepatan (tetapi apakah fungsi memanggil waktu sebenarnya merupakan bagian besar dari waktu Anda menjalankan ???).
Namun, jika Anda menjalankan banyak proses yang semuanya sering memanggil pustaka yang sama, Anda bisa menghemat garis cache (dan dengan demikian menang saat menjalankan kinerja) saat menggunakan tautan dinamis relatif menggunakan tautan statis. (Kecuali OS modern cukup pintar untuk melihat segmen identik dalam binari yang terhubung secara statis. Tampaknya sulit, ada yang tahu?)
Masalah lain: memuat waktu. Anda membayar biaya pemuatan di beberapa titik. Saat Anda membayar biaya ini tergantung pada cara kerja OS serta tautan apa yang Anda gunakan. Mungkin Anda lebih suka menunda membayarnya sampai Anda tahu Anda membutuhkannya.
Perhatikan bahwa tautan statis-vs-dinamis biasanya bukan masalah pengoptimalan, karena keduanya melibatkan kompilasi terpisah hingga ke file objek. Namun, ini tidak diperlukan: kompiler pada prinsipnya dapat "mengkompilasi" "perpustakaan statis" pada bentuk AST yang dicerna pada awalnya, dan "menautkan" mereka dengan menambahkan AST itu dengan yang dihasilkan untuk kode utama, sehingga memperkuat optimisasi global. Tidak ada sistem yang saya gunakan melakukan ini, jadi saya tidak bisa mengomentari seberapa baik kerjanya.
Cara untuk menjawab pertanyaan kinerja selalu dengan menguji (dan menggunakan lingkungan pengujian sebanyak mungkin seperti lingkungan penyebaran).
sumber
1) didasarkan pada fakta bahwa memanggil fungsi DLL selalu menggunakan lompatan tidak langsung tambahan. Hari ini, ini biasanya diabaikan. Di dalam DLL ada beberapa overhead pada CPU i386, karena mereka tidak dapat menghasilkan kode posisi independen. Pada amd64, lompatan bisa relatif terhadap penghitung program, jadi ini merupakan peningkatan besar.
2) Ini benar. Dengan optimasi yang dipandu oleh profil, Anda biasanya dapat memenangkan sekitar 10-15 persen kinerja. Sekarang kecepatan CPU telah mencapai batasnya, mungkin layak untuk dilakukan.
Saya akan menambahkan: (3) linker dapat mengatur fungsi dalam pengelompokan yang lebih efisien cache, sehingga tingkat cache yang mahal diabaikan. Ini juga mungkin sangat mempengaruhi waktu startup aplikasi (berdasarkan hasil yang saya lihat dengan kompiler Sun C ++)
Dan jangan lupa bahwa dengan DLL tidak ada penghapusan kode mati yang dapat dilakukan. Bergantung pada bahasanya, kode DLL mungkin juga tidak optimal. Fungsi virtual selalu virtual karena kompiler tidak tahu apakah klien menimpanya.
Untuk alasan ini, jika tidak ada kebutuhan nyata untuk DLL, maka gunakan saja kompilasi statis.
EDIT (untuk menjawab komentar, dengan garis bawah pengguna)
Berikut ini adalah sumber yang bagus tentang masalah kode posisi independen http://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-share-libraries/
Sebagaimana dijelaskan x86 tidak memilikinya AFAIK untuk hal lain selain rentang lompat 15 bit dan tidak untuk lompatan dan panggilan tanpa syarat. Itu sebabnya fungsi (dari generator) yang memiliki lebih dari 32 ribu selalu menjadi masalah dan membutuhkan trampolin yang disematkan.
Tetapi pada OS x86 populer seperti Linux Anda tidak perlu peduli jika file .so / DLL tidak dihasilkan dengan
gcc
switch-fpic
(yang memberlakukan penggunaan tabel lompat tidak langsung). Karena jika Anda tidak melakukannya, kode itu hanya diperbaiki seperti penghubung biasa akan memindahkannya. Tetapi ketika melakukan ini membuat segmen kode tidak dapat dibagikan dan akan membutuhkan pemetaan penuh kode dari disk ke memori dan menyentuhnya semua sebelum dapat digunakan (mengosongkan sebagian besar cache, memukul TLB) dll. Ada waktu saat ini dianggap lambat.Jadi Anda tidak akan mendapat manfaat lagi.
Saya tidak ingat apa OS (Solaris atau FreeBSD) memberi saya masalah dengan saya Unix membangun sistem karena saya hanya tidak melakukan hal ini dan bertanya-tanya mengapa jatuh sampai saya diterapkan
-fPIC
untukgcc
.sumber
Tautan dinamis adalah satu-satunya cara praktis untuk memenuhi beberapa persyaratan lisensi seperti LGPL .
sumber
Saya setuju dengan poin dnmckee menyebutkan, ditambah:
sumber
Salah satu alasan untuk melakukan build yang terhubung secara statis adalah untuk memverifikasi bahwa Anda memiliki penutupan penuh untuk dieksekusi, yaitu bahwa semua referensi simbol diselesaikan dengan benar.
Sebagai bagian dari sistem besar yang sedang dibangun dan diuji menggunakan integrasi terus menerus, tes regresi malam dijalankan menggunakan versi yang terhubung secara statis dari executable. Kadang-kadang, kita akan melihat bahwa sebuah simbol tidak akan menyelesaikan dan tautan statis akan gagal meskipun eksekusi yang terhubung secara dinamis akan berhasil terhubung.
Ini biasanya terjadi ketika simbol yang berada jauh di dalam lib bersama memiliki nama yang salah sehingga tidak akan terhubung secara statis. Dynamic linker tidak sepenuhnya menyelesaikan semua simbol, terlepas dari menggunakan evaluasi mendalam-pertama atau luas-pertama, sehingga Anda dapat menyelesaikan dengan executable terkait secara dinamis yang tidak memiliki penutupan penuh.
sumber
1 / Saya pernah mengerjakan proyek di mana penautan dinamis vs penautan statis dipatok dan perbedaannya tidak cukup kecil untuk beralih ke penautan dinamis (saya bukan bagian dari tes, saya hanya tahu kesimpulannya)
2 / Tautan dinamis sering dikaitkan dengan PIC (Position Independent Code, kode yang tidak perlu dimodifikasi tergantung pada alamat di mana ia dimuat). Bergantung pada arsitektur, PIC dapat membawa perlambatan lain tetapi diperlukan untuk mendapatkan manfaat dari berbagi pustaka yang terhubung secara dinamis antara dua yang dapat dieksekusi (dan bahkan dua proses yang dapat dieksekusi yang sama jika OS menggunakan pengacakan alamat beban sebagai ukuran keamanan). Saya tidak yakin bahwa semua OS memungkinkan untuk memisahkan dua konsep, tetapi Solaris dan Linux melakukan dan ISTR yang dilakukan HP-UX juga.
3 / Saya pernah mengerjakan proyek lain yang menggunakan tautan dinamis untuk fitur "tambalan mudah". Tapi ini "tambalan mudah" membuat distribusi perbaikan kecil sedikit lebih mudah dan rumit satu mimpi buruk versi. Kami sering berakhir dengan harus mendorong segalanya dan harus melacak masalah di situs pelanggan karena versi yang salah adalah token.
Kesimpulan saya adalah bahwa saya menggunakan tautan statis yang dikecualikan:
untuk hal-hal seperti plugin yang bergantung pada tautan dinamis
ketika berbagi itu penting (pustaka besar yang digunakan oleh banyak proses pada saat yang sama seperti runtime C / C ++, pustaka GUI, ... yang sering dikelola secara independen dan ABI didefinisikan dengan ketat)
Jika seseorang ingin menggunakan "tambalan mudah", saya berpendapat bahwa perpustakaan harus dikelola seperti perpustakaan besar di atas: mereka harus hampir independen dengan ABI yang ditentukan yang tidak boleh diubah oleh perbaikan.
sumber
Ini membahas dengan sangat rinci tentang perpustakaan bersama di linux dan implikasi kinerja.
sumber
Pada sistem mirip Unix, penautan dinamis dapat mempersulit 'root' untuk menggunakan aplikasi dengan pustaka bersama yang dipasang di lokasi yang tidak terjangkau. Ini karena tautan dinamis umumnya tidak akan memperhatikan LD_LIBRARY_PATH atau yang setara untuk proses dengan hak akses root. Terkadang, tautan statis menghemat hari.
Atau, proses instalasi harus menemukan perpustakaan, tetapi itu bisa menyulitkan beberapa versi perangkat lunak untuk hidup berdampingan pada mesin.
sumber
LD_LIBRARY_PATH
bukanlah hambatan untuk menggunakan perpustakaan bersama, setidaknya tidak di GNU / Linux. Misalnya jika Anda meletakkan pustaka bersama di direktori../lib/
relatif ke file program, maka dengan rantai alat GNU opsi linker-rpath $ORIGIN/../lib
akan menentukan pencarian pustaka dari lokasi relatif itu. Anda kemudian dapat dengan mudah memindahkan aplikasi bersama dengan semua pustaka bersama yang terkait. Menggunakan trik ini, tidak ada masalah memiliki beberapa versi aplikasi dan pustaka baik (dengan asumsi mereka terkait, jika tidak Anda bisa menggunakan tautan simbolik)./etc/ld.so.conf
untuk kasus itu.Ini sangat sederhana, sungguh. Ketika Anda membuat perubahan dalam kode sumber Anda, apakah Anda ingin menunggu 10 menit untuk membangun atau 20 detik? Hanya 20 detik yang bisa saya pakai. Di luar itu, saya mengeluarkan pedang atau mulai berpikir tentang bagaimana saya dapat menggunakan kompilasi terpisah dan menghubungkannya untuk membawanya kembali ke zona nyaman.
sumber
Contoh terbaik untuk penautan dinamis adalah, ketika perpustakaan bergantung pada perangkat keras yang digunakan. Pada zaman kuno perpustakaan C matematika diputuskan bersifat dinamis, sehingga setiap platform dapat menggunakan semua kemampuan prosesor untuk mengoptimalkannya.
Contoh yang lebih baik lagi adalah OpenGL. OpenGl adalah API yang diimplementasikan secara berbeda oleh AMD dan NVidia. Dan Anda tidak dapat menggunakan implementasi NVidia pada kartu AMD, karena perangkat kerasnya berbeda. Anda tidak dapat menautkan OpenGL secara statis ke program Anda, karena itu. Tautan dinamis digunakan di sini untuk memungkinkan API dioptimalkan untuk semua platform.
sumber
Menghubungkan dinamis memerlukan waktu ekstra bagi OS untuk menemukan perpustakaan dinamis dan memuatnya. Dengan penghubung statis, semuanya bersama-sama dan ini adalah satu tembakan ke memori.
Juga, lihat Neraka DLL . Ini adalah skenario di mana DLL yang memuat OS bukan yang datang dengan aplikasi Anda, atau versi yang diharapkan aplikasi Anda.
sumber
Masalah lain yang belum dibahas adalah memperbaiki bug di perpustakaan.
Dengan tautan statis, Anda tidak hanya harus membangun kembali perpustakaan, tetapi juga harus menautkan kembali dan mendistribusikan kembali yang dapat dieksekusi. Jika perpustakaan hanya digunakan dalam satu executable, ini mungkin tidak menjadi masalah. Tetapi semakin banyak yang dapat dieksekusi yang perlu ditata kembali dan didistribusikan, semakin besar rasa sakitnya.
Dengan tautan dinamis, Anda hanya membangun kembali dan mendistribusikan kembali perpustakaan dinamis dan Anda selesai.
sumber
tautan statis memberi Anda hanya satu exe, inorder untuk membuat perubahan yang Anda butuhkan untuk mengkompilasi ulang seluruh program Anda. Sedangkan dalam penautan dinamis Anda perlu membuat perubahan hanya ke dll dan ketika Anda menjalankan exe Anda, perubahan akan diambil saat runtime. Lebih mudah untuk memberikan pembaruan dan perbaikan bug dengan tautan dinamis (misalnya: windows).
sumber
Ada sejumlah besar dan semakin banyak sistem di mana tingkat penghubung statis yang ekstrem dapat memiliki dampak positif yang sangat besar pada aplikasi dan kinerja sistem.
Saya merujuk pada apa yang sering disebut "embedded system", banyak di antaranya sekarang semakin menggunakan sistem operasi untuk tujuan umum, dan sistem ini digunakan untuk segala yang dapat dibayangkan.
Contoh yang sangat umum adalah perangkat yang menggunakan sistem GNU / Linux menggunakan Busybox . Saya telah mengambil ini secara ekstrim dengan NetBSD dengan membangun image sistem i386 (32-bit) yang dapat di-boot yang mencakup kernel dan filesystem root-nya, yang terakhir yang berisi satu
crunchgen
biner yang dihubungkan dengan statis tunggal (dengan ) dengan tautan keras ke semua program itu sendiri berisi semua (baik pada hitungan terakhir 274) dari program sistem fitur lengkap standar (kebanyakan kecuali toolchain), dan ukurannya kurang dari 20 mega byte (dan mungkin berjalan sangat nyaman dalam sistem dengan hanya 64MB memori (bahkan dengan sistem file root tidak terkompresi dan seluruhnya dalam RAM), meskipun saya belum dapat menemukan satu yang sangat kecil untuk mengujinya).Telah disebutkan dalam posting sebelumnya bahwa waktu start-up dari binari yang terhubung statis lebih cepat (dan itu bisa jauh lebih cepat), tetapi itu hanya sebagian dari gambar, terutama ketika semua kode objek dihubungkan ke dalam yang sama file, dan bahkan lebih terutama ketika sistem operasi mendukung permintaan paging kode langsung dari file yang dapat dieksekusi. Dalam skenario ideal ini waktu startup program secara harfiah dapat diabaikan karena hampir semua halaman kode sudah ada dalam memori dan digunakan oleh shell (dan
init
proses latar belakang lainnya yang mungkin sedang berjalan), bahkan jika program yang diminta belum pernah dijalankan sejak boot karena mungkin hanya satu halaman memori perlu dimuat untuk memenuhi persyaratan runtime program.Namun itu masih belum keseluruhan cerita. Saya juga biasanya membangun dan menggunakan instalasi sistem operasi NetBSD untuk sistem pengembangan penuh saya dengan menghubungkan semua binari secara statis. Meskipun ini membutuhkan lebih banyak ruang disk yang lebih besar (~ total 6.6GB untuk x86_64 dengan segalanya, termasuk toolchain dan X11 yang terhubung statis) (terutama jika seseorang menyimpan tabel simbol debug penuh tersedia untuk semua program lain ~ 2.5GB), hasilnya masih berjalan lebih cepat secara keseluruhan, dan untuk beberapa tugas bahkan menggunakan memori lebih sedikit daripada sistem yang terhubung-dinamis khas yang dimaksudkan untuk berbagi halaman kode perpustakaan. Disk itu murah (bahkan fast disk), dan memori ke cache yang sering digunakan file disk juga relatif murah, tetapi siklus CPU sebenarnya tidak, dan membayar
ld.so
biaya startup untuk setiap proses yang dimulai setiapwaktu mulai akan mengambil jam dan siklus siklus CPU jauh dari tugas yang memerlukan memulai banyak proses, terutama ketika program yang sama digunakan berulang-ulang, seperti kompiler pada sistem pengembangan. Program-program toolchain terkait statis dapat mengurangi waktu membangun multi-arsitektur seluruh OS untuk sistem saya selama berjam-jam . Saya belum membuat toolchain ke dalamcrunchgen
biner tunggal saya , tetapi saya curiga kalau saya lakukan, akan ada lebih banyak waktu untuk membangun waktu karena kemenangan untuk cache CPU.sumber
Menghubungkan statis termasuk file yang dibutuhkan program dalam satu file yang dapat dieksekusi.
Tautan dinamis adalah apa yang Anda anggap biasa, itu membuat executable yang masih memerlukan DLL dan semacamnya berada di direktori yang sama (atau DLL bisa berada di folder sistem).
(DLL = perpustakaan tautan dinamis )
Eksekusi yang terhubung secara dinamis disusun lebih cepat dan tidak terlalu banyak sumber daya.
sumber
Static linking
adalah proses dalam waktu kompilasi ketika konten yang ditautkan disalin ke dalam biner primer dan menjadi satu biner.Cons:
Dynamic linking
adalah proses runtime ketika konten yang ditautkan dimuat. Teknik ini memungkinkan untuk:ABI
stabilitas [Tentang]Cons:
sumber