静的リンクと動的リンク


399

特定の状況で動的リンクよりも静的リンクを選択する、またはその逆を選択する、説得力のあるパフォーマンス上の理由はありますか?私は次のことを聞いたり読んだりしましたが、その真実性を保証する主題については十分に知りません。

1)静的リンクと動的リンクの実行時パフォーマンスの違いは、通常は無視できます。

2)(1)は、プロファイルデータを使用してプログラムのホットパスを最適化するプロファイリングコンパイラーを使用している場合は当てはまりません。静的リンクを使用すると、コンパイラーはコードとライブラリコードの両方を最適化できるためです。動的リンクを使用すると、コードのみを最適化できます。ほとんどの時間がライブラリコードの実行に費やされている場合、これは大きな違いを生む可能性があります。それ以外の場合は、(1)が引き続き適用されます。


59
「静的リンクを使用すると、コンパイラはライブラリコードを最適化できます」が、それもそれをコンパイルする場合のみです。コンパイル済みのオブジェクトファイルにリンクするだけの場合、コンパイラはそれらを最適化する機会がありません。

3
それが本当である場合、あなたは正しいですが、現代のコンパイラでどれほど本当かについていくつかの疑問があります。誰かがこれを何らかの方法で検証できれば、それは素晴らしいでしょう。
Eloff

5
ネイティブコードにコンパイルするコンパイラー(ほとんどのC / C ++コンパイラーと同様)を使用すると、コードを最適化する機会がなくなります。コードがいくつかの中間言語(.Net ILなど)にコンパイルされている場合、ライブラリが読み込まれるときにJITコンパイラが呼び出され、ネイティブコードにコンパイルされます。その最終的なコンパイルは、JITコンパイラが進化するにつれて、時間とともに次第に良くなる可能性があります。
タリドン

3
@Eloff:VS2008は、LTCGを有効にした場合とまったく同じです。(ただし、libファイルは巨大になります。)私はそれをいじくり回しました。「コンパイラーが私に何ができるか」に興味がある人にとって、それは驚くべきことです。
peterchen 2010年

回答:


348
  • 動的リンクにより、総リソース消費量削減できます(複数のプロセスが同じライブラリを共有している場合(もちろん、「同じ」のバージョンを含む)))。私は、これがほとんどの環境でその存在を駆動する議論であると信じています。ここで「リソース」には、ディスク容量、RAM、キャッシュ容量が含まれます。もちろん、動的リンカーの柔軟性が不十分な場合は、DLL hellのリスクがあります
  • 動的リンクとは、バグ修正とライブラリーへのアップグレードが伝搬何も出荷することなく製品を改善することを意味します
  • プラグインは常に動的リンクを要求します。
  • 静的リンクとは、コードが非常に限られた環境(初期のブートプロセス、またはレスキューモード)で実行できることを意味します。
  • 静的リンクを使用すると、バイナリをさまざまなユーザー環境に配布やすくなります(リソースを大量に消費するプログラムを送信する代わりに)。
  • 静的リンクは、わずかに可能より高速な起動時間を、これはあなたのプログラムのサイズと複雑さの両方にある程度依存し、 OSのロード戦略の詳細に。

コメントやその他の回答に非常に関連性の高い提案を含めるための編集。これを打破する方法は、実行する予定の環境に大きく依存します。最小限の組み込みシステムには、動的リンクをサポートするための十分なリソースがない場合があります。わずかに大きい小さなシステムは、動的リンクをサポートするRAMが十分に小さいため、動的リンクをサポートする可能性があります。Mark氏が指摘するように、本格的なコンシューマPCには膨大なリソースがあり、利便性の問題がこの問題についてのあなたの考えを駆り立てることができるでしょう。


パフォーマンスと効率の問題に対処するには:状況によって異なります。

古典的に、動的ライブラリは、ある種の接着層を必要とします。これは、関数のアドレス指定で二重ディスパッチまたは間接的な追加の層を意味することが多く、少し速度がかかる場合があります(ただし、関数呼び出し時間は実際には実行時間の大部分ですか???)。

ただし、すべてが同じライブラリを頻繁に呼び出す複数のプロセスを実行している場合、静的リンクを使用する場合と比べて動的リンクを使用する場合は、結果的にキャッシュラインを節約することになります(したがって、実行パフォーマンスが向上します)。(最近のOSが、静的にリンクされたバイナリの同一のセグメントに気付くほど賢い場合を除きます。難しいと思われますか?)

別の問題:読み込み時間。ある時点で積載費用を支払います。このコストを支払う時期は、OSの動作と使用するリンクによって異なります。多分あなたはあなたがそれを必要とするまであなたがそれを支払うことを延期したいと思うでしょう。

static-vs-dynamicリンクはどちらもオブジェクトファイルへの個別のコンパイルを伴うため、伝統的に最適化の問題ではないことに注意してください。ただし、これは必須ではありません。コンパイラは原則として、最初にダイジェストASTフォームに「静的ライブラリ」を「コンパイル」し、メインコード用に生成されたものにそれらのASTを追加することでそれらを「リンク」し、グローバルな最適化を実現できます。私が使用しているシステムはどれもこれを実行しないため、どのように機能するかについてコメントすることはできません。

パフォーマンスの質問に答える方法は常にテストすることです(そして、可能な限りデプロイメント環境と同じようにテスト環境を使用します)。


24
リソースの消費は基本的にはコードスペースであり、時間が経つにつれ、問題はますます少なくなります。500Kのライブラリを5つのプロセス間で共有すると、2MBの節約になります。これは、3GBのRAMの0.1%未満です。
Mark Ransom、2010年

3
ライブラリが同じ仮想マッピング(すべてのプロセスで同じ物理アドレスと仮想アドレス)も共有している場合、ダイナミックリンクもプロセッサのMMUのTLBスロットを保存しませんか?
Zan Lynx

6
また、ダイナミックリンクにより、バグのあるライブラリコードをより優れたバージョンに簡単に更新できます。
Zan Lynx

89
@Zanまた、バグのあるコードを作業バージョンに簡単に追加できます。

6
「プラグインは常に動的リンクを必要とします。」不正解です。AppleのAudioUnitsなどの一部のプラグインモデルでは、プラグインを別のプロセスで実行し、IPCを使用できます。これは、プラグインの動的リンクのより安全な代替手段です(プラグインはホストをクラッシュできません)。回答を「プラグインには動的リンクが必要な場合があります」などに更新することを提案します。
テイラー

68

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は15ビットジャンプ範囲以外には何のAFAIKも持っておらず、無条件ジャンプや呼び出しのためのものではありません。そのため、32Kを超える(ジェネレーターからの)関数は常に問題であり、埋め込みトランポリンが必要でした。

しかし、Linuxのような一般的なx86 OSでは、.so / DLLファイルがgccスイッチで生成されていないかどうかを気にする必要はありません-fpic(間接ジャンプテーブルの使用を強制します)。そうしないと、通常のリンカがコードを再配置するようにコードが修正されるだけです。ただし、これを行うと、コードセグメントが共有不可になり、コードをディスクからメモリに完全にマッピングして、使用する前にすべてを操作する必要があります(ほとんどのキャッシュを空にし、TLBにヒットします)など。これが遅いと考えられたとき。

だからあなたはもう何の利益もないでしょう。

どのOS(SolarisまたはFreeBSD)がUnixビルドシステムで問題を起こしたのか思い出せません。これを行っていないだけで、に適用-fPICするまでなぜクラッシュするのか疑問に思いましたgcc


4
私がこの回答を気に入ったのは、質問で私が提起したポイントに取り組む唯一の回答だったからです。
Eloff

それらのDLLの技術についての参照、およびさまざまなオペレーティングシステム間の比較が興味深いです。
UncleZeiv 2010

正常に見えますが、CPU速度は確実に限界に達していません。
アイディアカピ2015

67

動的リンクは、LGPLなどの一部のライセンス要件を満たす唯一の実用的な方法です。


17
エンドユーザーがLGPLされたコードに再リンクできる限り(たとえば、ソフトウェアにソースコードまたはコンパイル済みオブジェクトファイルを提供するため)、静的リンクで問題ありません。。さらに、ソフトウェアが内部で使用される場合(つまり、組織内でのみ使用され、配布されない場合)、静的にリンクできます。これは、たとえばサーバーが配布されていないサーバーソフトウェアに適用されます。
JBentley 2014年

3
わかりません。あなたが書いたものを評価するために、より多くの情報源(またはより詳細な情報)をくれませんか?
Baskaya

4
@Thornは、LGPLライセンスのセクション 4.d + eを参照してください。ユーザーにリンクを要求する形式で配布するか、共有(動的)ライブラリを配布する必要があります。
Mark Ransom

46

私はdnmckeeが述べている点に同意し、それに加えて:

  • 静的にリンクされたアプリケーションは、不足している場合や間違った場所にインストールされた場合に問題を引き起こす可能性のある追加のファイル依存関係(.dll / .so)が少ないかまったくないので、展開が容易になる場合があります。

6
GoogleのGoコンパイラは、主にこの理由でバイナリを静的にコンパイルするだけであることは注目に値します。
Hut8 2014年

34

静的にリンクされたビルドを行う1つの理由は、実行可能ファイルが完全に閉じていること、つまりすべてのシンボル参照が正しく解決されていることを確認することです。

継続的な統合を使用して構築およびテストされていた大規模なシステムの一部として、実行可能ファイルの静的にリンクされたバージョンを使用して、毎晩の回帰テストが実行されました。場合によっては、動的にリンクされた実行可能ファイルが正常にリンクしても、シンボルが解決されず、静的リンクが失敗することがあります。

これは通常、共有ライブラリーの奥深くにあるシンボルにスペルミスの名前があり、静的にリンクしない場合に発生していました。動的リンカーは、深さ優先または幅優先の評価の使用に関係なく、すべてのシンボルを完全には解決しないため、完全に閉じていない動的にリンクされた実行可能ファイルで仕上げることができます。


1
非常に良い点です。私は最近、いくつかのコードを使用してこれを実行しようとしましたが、すべてを静的にコンパイルすると、驚くほど煩わしいことが判明し、あきらめました
UncleZeiv

21

1 /私は動的リンクと静的リンクのベンチマークが行われ、動的リンクに切り替えるのに十分な差がないと判断されたプロジェクトに参加しました(私はテストに参加していませんでした。結論はわかっています)

2 /動的リンクはしばしばPIC(位置独立コード、ロードされるアドレスに応じて変更する必要のないコード)に関連付けられています。アーキテクチャによっては、PICは別の速度低下をもたらす可能性がありますが、動的にリンクされたライブラリを2つの実行可能ファイル(OSがセキュリティ対策としてロードアドレスのランダム化を使用している場合は、同じ実行可能ファイルの2つのプロセスでさえも)を共有する利点を得るために必要です。すべてのOSが2つの概念を分離できるかどうかはわかりませんが、SolarisとLinuxでは可能であり、HP-UXではISTRも可能です。

3 /「簡単なパッチ」機能に動的リンクを使用する他のプロジェクトに参加しました。しかし、この「簡単なパッチ」により、小さな修正の配布が少し簡単になり、複雑な修正のバージョン管理が悪夢のようになります。間違ったバージョンがトークンだったため、すべてをプッシュする必要があり、顧客サイトで問題を追跡する必要があった。

私の結論は、以下を除いて静的リンクを使用したということです:

  • 動的リンクに依存するプラグインのようなもの

  • 共有が重要な場合(C / C ++ランタイムのような複数のプロセスで同時に使用される大きなライブラリ、GUIライブラリなど、多くの場合、個別に管理され、ABIが厳密に定義されています)

「簡単なパッチ」を使用したい場合、ライブラリーは上記の大きなライブラリーのように管理する必要があると私は主張します。


1
非PICプロセッサまたは高価なPICプロセッサの一部のOSは、メモリ内の特定のアドレスに読み込まれる動的ライブラリを準備し、それが可能であれば、ライブラリのコピーをそれにリンクするすべてのプロセスにマッピングします。これにより、PICのオーバーヘッドが大幅に削減されます。少なくともOS Xと一部のLinuxディストリビューションではこれを行っていますが、Windowsについてはわかりません。
Andrew McGregor

Andrewのおかげで、一部のLinuxディストリビューションでこれが使用されていることを知りませんでした。私が従うことができる参照や、詳細を調べるために検索できるキーワードはありますか?(FWIW私はWindowsがこれの変種をやっていると聞いていましたが、Windowsは私の能力の範囲から遠すぎて言及できません)。
AProgrammer

あなたが探しているキーワードは「プリリンク」だと思います。これは、ライブラリを特定のアドレスにすばやくロードして、プログラムの起動を高速化する準備をします。
Blaisorblade 2013年

20

これは、Linux上の共有ライブラリとパフォーマンスへの影響について非常に詳細に説明しています。


3
DrepperのDSO howtoにリンクするための+1。Linuxでライブラリを作成するすべての人が読む必要があります。
janneb

10

Unixライクなシステムでは、動的リンクにより、「ルート」が、共有ライブラリが離れた場所にインストールされたアプリケーションを使用することが困難になる場合があります。これは、動的リンカーは一般に、ルート権限を持つプロセスのLD_LIBRARY_PATHまたは同等のものに注意を払わないためです。時には、静的リンクがその日を節約することもあります。

または、インストールプロセスでライブラリを特定する必要がありますが、その場合、複数のバージョンのソフトウェアをマシン上で共存させることが困難になる可能性があります。


1
この点LD_LIBRARY_PATHは、少なくともGNU / Linuxでは、共有ライブラリの使用を妨げるものではありません。たとえば、共有ライブラリを../lib/プログラムファイルに相対的なディレクトリに配置した場合、GNUツールチェーンを使用すると、リンカーオプション-rpath $ORIGIN/../libはその相対的な場所からライブラリを検索することを指定します。その後、関連するすべての共有ライブラリとともにアプリケーションを簡単に再配置できます。このトリックを使用すると、アプリケーションとライブラリの複数のバージョンでも問題はありません(シンボリックリンクを使用できない場合は、それらが関連していると想定します)。
FooF 2012年

> root権限を持つプロセスの場合。root以外のユーザーから実行されるsetuidプログラムについて話していると思いますが、そうでなければ意味がありません。そして、標準以外の場所にライブラリがあるsetuidバイナリは奇妙ですが、rootだけがそれらのプログラムをインストールできる/etc/ld.so.confため、その場合のために編集することもできます。
Blaisorblade 2013年

10

本当にシンプルです。ソースコードに変更を加えた場合、ビルドが完了するまで10分待機しますか、それとも20秒待機しますか?私が我慢できるのは20秒だけです。それを超えて、私は剣を出すか、別のコンパイルとリンクを使用してそれを快適ゾーンに戻す方法について考え始めます。


1
コンパイル速度の違いを実際にベンチマークしたことはありませんが、大幅に高速であればダイナミックリンクを使用します。Boostは、私のコンパイル時間に十分な悪いことをそのまま行います。
Eloff

9

動的リンクの最良の例は、ライブラリが使用されているハードウェアに依存している場合です。古代には、C数学ライブラリは動的であると決定され、各プラットフォームがすべてのプロセッサ機能を使用して最適化できるようになりました。

さらに良い例はOpenGLかもしれません。OpenGlは、AMDとNVidiaで実装が異なるAPIです。また、ハードウェアが異なるため、AMDカードでNVidia実装を使用することはできません。そのため、OpenGLをプログラムに静的にリンクすることはできません。ここではダイナミックリンクを使用して、APIをすべてのプラットフォーム用に最適化します。


8

動的リンクでは、OSが動的ライブラリを見つけてロードするために追加の時間が必要です。静的リンクを使用すると、すべてが一緒になり、メモリへのワンショットロードになります。

また、DLL Hellも参照してください。これは、OSがロードするDLLがアプリケーションに付属するDLLでも、アプリケーションが期待するバージョンでもないシナリオです。


1
DLL Hellを回避するためのさまざまな対策があることに注意してください。
ocodo 2012

5

まだ議論されていない別の問題は、ライブラリのバグを修正することです。

静的リンクでは、ライブラリを再構築するだけでなく、実行可能ファイルを再リンクして再配布する必要があります。ライブラリが1つの実行可能ファイルで使用されるだけの場合、これは問題ではない可能性があります。しかし、再リンクして再配布する必要のある実行可能ファイルが多いほど、痛みは大きくなります。

動的リンクを使用すると、動的ライブラリを再構築して再配布するだけで済みます。


2

静的リンクでは、プログラム全体を再コンパイルするために必要な変更を加えるために、単一のexeのみが提供されます。ダイナミックリンクではdllのみを変更する必要があり、exeを実行すると、変更は実行時に取得されます。ダイナミックリンク(例:ウィンドウ)を使用すると、更新やバグ修正を簡単に提供できます。


2

非常に高いレベルの静的リンクがアプリケーションとシステムパフォーマンスに多大なプラスの影響を与える可能性のある、膨大で増加する数のシステムがあります。

私はしばしば「組み込みシステム」と呼ばれるものを参照します。その多くは現在、汎用オペレーティングシステムをますます使用しており、これらのシステムは考えられるすべてのものに使用されています。

非常に一般的な例は、Busyboxを使用するGNU / Linuxシステムを使用するデバイスです。カーネルとそのルートファイルシステムの両方を含むブート可能なi386(32ビット)システムイメージを構築することにより、NetBSDでこれを極端にしました。後者には、crunchgenハードリンク付きの単一の静的リンク(バイ)バイナリが含まれています。自身が含まれているすべてのプログラムのすべてを(うまく最後のカウントで274)の標準的なフル機能のシステムプログラムの(ツールチェインを除くほとんど)、およびそれが20未満であるメガサイズのバイト(そしておそらく唯一の64メガバイトとシステムに非常に快適に動作しますメモリ(ルートファイルシステムが圧縮されておらず、完全にRAMにある場合でも)ですが、テストできるほど小さいものを見つけることができませんでした)。

以前の投稿で、静的にリンクされたバイナリの起動時間は高速である(そしてそれははるかに高速になる可能性がある)と述べられていますが、それは特にすべてのオブジェクトコードが同じにリンクされている場合、画像の一部にすぎませんファイル、さらに具体的には、オペレーティングシステムが実行可能ファイルから直接のコードのデマンドページングをサポートしている場合はなおさらです。この理想的なシナリオでは、要求されたプログラムが実行されていない場合でも、コードのほぼすべてのページが既にメモリにあり、シェル(および実行中の他のバックグラウンドプロセス)によって使用されているため、プログラムの起動時間は文字通り無視できます。initプログラムの実行時の要件を満たすためにロードする必要があるのはおそらく1ページのメモリのみであるため、ブート以降に実行されています。

しかし、それだけではまだ完全ではありません。また、私は通常、すべてのバイナリを静的リンクすることにより、完全な開発システム用のNetBSDオペレーティングシステムのインストールをビルドして使用します。これには非常に多くのディスクスペースが必要です(x86_64の場合、ツールチェーンと静的リンクされたX11を含むすべてで合計で6.6GBまで)(特に、すべてのプログラムで利用可能な完全なデバッグシンボルテーブルを別の〜2.5GBに維持している場合)全体的に高速で実行され、一部のタスクでは、ライブラリコードページを共有することを目的とする典型的なダイナミックリンクシステムよりも少ないメモリを使用します。ディスクは安価(高速ディスクでも)であり、頻繁に使用されるディスクファイルをキャッシュするメモリも比較的安価ですが、CPUサイクルは実際には安価ではなく、ld.soすべてのプロセスを開始するたびに起動コストを支払います特に、開発システムのコンパイラーなど、同じプログラムが何度も使用される場合、多くのプロセスを開始する必要があるタスクから数時間および数時間のCPUサイクルがかかります。静的にリンクされたツールチェーンプログラムは、システムのOS全体のマルチアーキテクチャビルド時間を数時間削減できます。私はまだツールチェーンをシングルcrunchgenバイナリにビルドする必要はありませんが、実行すると、CPUキャッシュの勝利によりビルド時間がさらに節約されると思います。


2

静的リンクには、プログラムが単一の実行可能ファイルに必要とするファイルが含まれます。

ダイナミックリンクは、通常と見なすものであり、DLLなどを同じディレクトリに置く必要がある実行可能ファイルを作成します(またはDLLがシステムフォルダーにある可能性があります)。

(DLL = ダイナミックリンクライブラリ)

動的にリンクされた実行可能ファイルはより速くコンパイルされ、リソースをあまり消費しません。


0

Static linking リンクされたコンテンツがプライマリバイナリにコピーされ、単一のバイナリになるコンパイル時のプロセスです。

短所:

  • コンパイル時間が長い
  • 出力バイナリが大きい

Dynamic linkingリンクされたコンテンツが読み込まれたときの実行時のプロセスです。この技術により、次のことが可能になります。

  • ABI安定性を向上させるプライマリバイナリを再コンパイルせずにリンクされたバイナリをアップグレードする[概要]
  • 単一の共有コピーを持っている

短所:

  • 開始時間が遅い(リンクされたコンテンツをコピーする必要があります)
  • 実行時にリンカーエラーがスローされる
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.