c++ - static library dynamic library




靜態鏈接與動態鏈接 (10)

在某些情況下,是否有任何令人信服的性能原因來選擇通過動態鏈接進行靜態鏈接,反之亦然? 我聽過或讀過以下內容,但我對這個主題不太了解,不能保證其準確性。

1)靜態鏈接和動態鏈接之間的運行時性能差異通常可以忽略不計。

2)(1)如果使用分析編譯器使用概要文件數據優化程序熱路徑,因為使用靜態鏈接,編譯器可以優化代碼和庫代碼。 通過動態鏈接,只有您的代碼可以被優化。 如果大部分時間用於運行庫代碼,這可能會產生很大的差異。 否則,(1)仍然適用。


1 /我一直在關於動態鏈接和靜態鏈接進行基準測試的項目,並且它們之間的差異不足以切換到動態鏈接(我不是測試的一部分,我只知道結論)

2 /動態鏈接通常與PIC(位置獨立代碼,不需要根據裝載地址修改的代碼)相關聯。 根據體系結構的不同,PIC可能會帶來另一個緩慢的變化,但為了在兩個可執行文件之間共享動態鏈接庫(如果操作系統使用加載地址隨機化作為安全措施,甚至是同一個可執行文件的兩個進程),也是必要的。 我不確定所有的操作系​​統是否允許將這兩個概念分開,但是Solaris和Linux可以和HP-UX也可以。

3 /我一直在使用動態鏈接進行“簡易補丁”功能的其他項目。 但是這個“簡單的補丁”使得小修補程序的分髮變得更容易一些,而且更複雜的是一個版本化的噩夢。 我們常常因為錯誤的版本是令牌而不得不推動所有事情,並且必須跟踪客戶現場的問題。

我的結論是,我使用了例外的靜態鏈接:

  • 適用於像動態鏈接這樣的插件

  • 當共享很重要時(同時使用多個進程的大型庫,如C / C ++運行庫,GUI庫,...通常獨立管理並且ABI嚴格定義)

如果有人想使用“簡易補丁”,我會爭辯說,這些庫必須像上面的大型庫那樣進行管理:它們必須幾乎是獨立的,並且必須不會被修復所改變。


1)基於調用DLL函數的事實總是使用額外的間接跳轉。 今天,這通常可以忽略不計。 在DLL內部,i386 CPU的開銷更大,因為它們不能生成位置無關的代碼。 在amd64上,跳轉可以相對於程序計數器,所以這是一個巨大的改進。

2)這是正確的。 通過分析指導進行優化,通常可以贏得10-15%的性能。 現在CPU速度已經達到了極限,這可能是值得的。

我會補充一下:(3)鏈接器可以以更高效的緩存分組來安排功能,以便將昂貴的緩存失誤降至最低。 它也可能會影響應用程序的啟動時間(基於我在Sun C ++編譯器中看到的結果)

並且不要忘記,使用DLL的時候,不需要執行死代碼。 根據不同的語言,DLL代碼可能不是最佳的。 虛擬函數總是虛擬的,因為編譯器不知道客戶端是否覆蓋它。

由於這些原因,如果沒有真正的DLL需求,那麼就使用靜態編譯。

編輯(通過用戶下劃線回答評論)

這是一個關於位置獨立代碼問題的好資源http://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/

正如所解釋的,x86沒有AFAIK,所以沒有15位跳轉範圍,也沒有用於無條件跳轉和調用。 這就是為什麼具有32K以上的功能(來自發電機)一直是個問題,需要嵌入蹦床。

但在像Linux這樣流行的x86操作系統上,如果SO / DLL文件不是使用gcc開關-fpic (強制使用間接跳轉表)生成的,則不需要關心。 因為如果你不這樣做,那麼代碼就像正常的鏈接器那樣被修復就可以重新定位它。 但是,這樣做會使代碼段不可共享,並且需要將代碼從磁盤完全映射到內存中,並在它可以使用之前將其全部觸及(清空大部分緩存,打擊TLB)等等。有時候當這被認為是慢...太慢。

所以你不會再有任何好處了。

我不記得OS(Solaris或FreeBSD)給我的Unix gcc系統帶來了什麼問題,因為我只是沒有這樣做,並且想知道為什麼它會崩潰,直到我將-fPIC應用於gcc


動態鏈接是滿足某些許可要求(如LGPL )的唯一實用方法。


動態鏈接的最佳例子是,當庫依賴於使用的硬件時。 在古代,C數學庫被認為是動態的,因此每個平台都可以使用所有處理器功能來優化它。

一個更好的例子可能是OpenGL。 OpenGl是一種由AMD和NVidia實施不同的API。 而且你不能在AMD卡上使用NVidia實現,因為硬件不同。 因為這個原因,你不能將OpenGL靜態地鏈接到你的程序中。 此處使用動態鏈接來讓API針對所有平台進行優化。


在類Unix系統上,動態鏈接可能會使“root”使用應用程序的共享庫安裝在偏遠的位置上變得困難。 這是因為動態鏈接器通常不會關注具有root權限的進程的LD_LIBRARY_PATH或其等價物。 有時候,靜態鏈接節省了一天的時間。

或者,安裝過程必須找到這些庫,但這可能會使多個版本的軟件難以在機器上共存。


執行靜態鏈接構建的一個原因是驗證是否已完全關閉可執行文件,即所有符號引用都已正確解析。

作為使用持續集成構建和測試的大型系統的一部分,夜間回歸測試使用靜態鏈接版本的可執行文件運行。 偶爾,我們會看到一個符號不會解析,即使動態鏈接的可執行文件鏈接成功,靜態鏈接也會失敗。

這通常發生在深度位於共享庫內的符號具有拼寫錯誤名稱時,因此不會進行靜態鏈接。 無論使用深度優先還是廣度優先評估,動態鏈接程序都不會完全解析所有符號,因此您可以完成一個沒有完全關閉的動態鏈接可執行文件。


我贊同dnmckee提到的觀點,再加上:

  • 靜態鏈接的應用程序可能更容易部署,因為其他文件依賴項(.dll / .so)較少或不存在,這些文件依賴項(.dll / .so)可能會在缺少或安裝在錯誤位置時導致問題。

有大量的系統,其中極端水平的靜態鏈接可以對應用程序和系統性能產生巨大的積極影響。

我指的是通常所說的“嵌入式系統”,其中許多現在越來越多地使用通用操作系統,並且這些系統被用於可想像的一切。

一個非常常見的例子是使用Busybox GNU / Linux系統的設備。 我已經通過構建一個可引導的i386(32位)系統映像,包括一個內核及其根文件系統,後者包含一個靜態鏈接(通過crunchgen )二進製文件與硬鏈接到所有程序本身包含全部標準全功能係統程序(除了工具鏈以外的所有程序)(最後計數為274),並且它的大小小於20 字節(並且可能在只有系統運行非常舒適64MB的內存(即使是未壓縮的根文件系統,完全在內存中),儘管我一直無法找到一個非常小的內存來測試它)。

在早期的文章中已經提到,靜態鏈接二進製文件的啟動時間更快(並且速度可能更快),但這只是圖片的一部分,尤其是當所有目標代碼鏈接到相同文件,尤其是當操作系統支持從可執行文件直接請求分頁代碼時。 在這種理想情況下,程序的啟動時間幾乎可以忽略不計,因為幾乎所有的代碼頁都已經在內存中並被shell使用(並且init可能正在運行的其他後台進程),即使請求的程序有因為可能只有一頁內存需要加載以滿足程序的運行時需求,所以從未啟動。

但是,這仍然不是全部。 我通常還會通過靜態鏈接所有二進製文件來為我的完整開發系統構建和使用NetBSD操作系統安裝。 儘管這佔用了大量的磁盤空間(對於x86_64來說總共大約6.6GB,包括工具鍊和X11靜態鏈接)(特別是如果一個程序可以為所有程序提供另一個2.5GB的全面調試符號表),結果仍然會整體運行速度更快,對於某些任務,甚至比使用共享庫代碼頁的典型動態鏈接系統佔用更少的內存。 磁盤價格便宜(甚至是快速磁盤),而且用於緩存常用磁盤文件的內存也相對便宜,但CPU週期實際上並非如此,為每次啟動的每個進程支付ld.so啟動成本需要花費數小時小時的CPU週期遠離需要啟動多個進程的任務,特別是當相同的程序反複使用時,例如開發系統上的編譯器。 靜態鏈接的工具鏈程序可以在幾個小時內為我的系統減少整個OS的多架構構建時間。 我還沒有將工具鏈構建到我的單個crunchgen二進製文件中,但是我懷疑當我這樣做時,由於CPU緩存的勝利,將節省更多的構建時間。


靜態鏈接包括程序在單個可執行文件中需要的文件。

動態鏈接就是你認為的通常的東西,它使得仍然需要DLL的可執行文件位於同一目錄中(或者DLL可能位於系統文件夾中)。

(DLL = 動態鏈接庫)

動態鏈接的可執行文件編譯速度更快,而且不像資源沉重。


靜態鏈接只給你一個exe文件,為了做出改變你需要重新編譯你的整個程序。 而在動態鏈接中,只需要對dll進行更改,而在運行exe時,更改將在運行時提取。通過動態鏈接(例如:windows)更容易提供更新和錯誤修復。





dynamic-linking