パフォーマンス分析が予想より少し長くなったので、別の回答に入れました。ピーターの回答は公式として受け入れますが、それは私が自分で測定を実行する動機付けに最も役立ち、測定する価値があるかもしれないものについて最もインスピレーションを与えたので、測定が欠けていました.
分析:
これまでに言及された具体的な欠点はすべて、ある種類の別のパフォーマンスに焦点を当てているように見えますが、実際の定量的データは欠落していました。私は次のいくつかの測定を行いました:
- IDE でソリューションをロードする時間
- IDE でコンパイルする時間
- アセンブリの読み込み時間 (アプリケーションの読み込みにかかる時間)
- 失われたコードの最適化 (アルゴリズムの実行にかかる時間)
この分析では、一部の人々が回答で言及している「設計の品質」を完全に無視しています。これは、品質がこのトレードオフの変数であるとは考えていないためです。私は、開発者が何よりもまず、可能な限り最高の設計を実現したいという欲求に基づいて実装を行うことを想定しています。ここでのトレードオフは、(ある程度の) パフォーマンスのために、設計が厳密に要求するよりも大きなアセンブリに機能を集約する価値があるかどうかです。
アプリケーションの構造:
私が作成したアプリケーションは、テストするために多数のソリューションとプロジェクトが必要だったため、やや抽象的です。そのため、それらすべてを生成するためのコードをいくつか書きました。
アプリケーションには 1000 個のクラスが含まれており、相互に継承する 5 つのクラスの 200 セットにグループ化されています。クラスの名前は、Axxx、Bxxx、Cxxx、Dxxx、および Exxx です。クラス A は完全に抽象的で、BD は部分的に抽象的で、それぞれ A のメソッドの 1 つをオーバーライドします。E は具体的です。メソッドは、E のインスタンスで 1 つのメソッドを呼び出すと、階層チェーンの複数の呼び出しが実行されるように実装されています。すべてのメソッド本体は十分に単純であるため、理論的にはすべてインライン化する必要があります。
これらのクラスは、2 つの次元に沿って 8 つの異なる構成でアセンブリ全体に分散されました。
- 組立数:10、20、50、100
- 切断方向: 継承階層全体 (AE が同じアセンブリに一緒に存在することはありません)、および継承階層に沿って
測定値はすべて正確に測定されているわけではありません。ストップウォッチによって行われたものもあり、誤差の範囲が大きくなっています。測定値は次のとおりです。
- VS2008 でソリューションを開く (ストップウォッチ)
- ソリューションのコンパイル (ストップウォッチ)
- IDE: 開始から最初に実行されたコード行までの時間 (ストップウォッチ)
- IDE: IDE 内の 200 個のグループごとに Exxx の 1 つをインスタンス化する時間 (コード内)
- IDE: IDE の各 Exxx で 100,000 回の呼び出しを実行する時間 (コード内)
- 最後の 3 つの 'In IDE' 測定値ですが、'Release' ビルドを使用したプロンプトから
結果:
VS2008 でソリューションを開く
----- in the IDE ------ ----- from prompt -----
Cut Asm# Open Compile Start new() Execute Start new() Execute
Across 10 ~1s ~2-3s - 0.150 17.022 - 0.139 13.909
20 ~1s ~6s - 0.152 17.753 - 0.132 13.997
50 ~3s 15s ~0.3s 0.153 17.119 0.2s 0.131 14.481
100 ~6s 37s ~0.5s 0.150 18.041 0.3s 0.132 14.478
Along 10 ~1s ~2-3s - 0.155 17.967 - 0.067 13.297
20 ~1s ~4s - 0.145 17.318 - 0.065 13.268
50 ~3s 12s ~0.2s 0.146 17.888 0.2s 0.067 13.391
100 ~6s 29s ~0.5s 0.149 17.990 0.3s 0.067 13.415
所見:
- アセンブリの数 (切断方向ではない) は、ソリューションを開くのにかかる時間にほぼ線形の影響を与えるようです。これは私には驚くべきことではありません。
- 約 6 秒で、ソリューションを開くのにかかる時間は、アセンブリの数を制限するための引数ではないように思えます。(ソース管理の関連付けがこの時間に大きな影響を与えたかどうかは測定しませんでした)。
- この測定では、コンパイル時間は線形より少し長くなります。これのほとんどは、アセンブリ間のシンボル解決ではなく、コンパイルのアセンブリごとのオーバーヘッドによるものだと思います。この軸に沿って、それほど重要でないアセンブリがより適切にスケーリングされることを期待しています。それでも、特にほとんどの場合、一部のアセンブリのみが再コンパイルを必要とすることに注意してください。
- かろうじて測定可能ですが、起動時間の顕著な増加が見られます。アプリケーションが最初に行うことは、コンソールに行を出力することです。「開始」時間は、実行の開始からこの行が表示されるまでにかかった時間です (最悪の場合でも正確に測定するには速すぎるため、これらは推定値であることに注意してください)。 .
- 興味深いことに、IDE アセンブリの読み込みの外側は、IDE の内側よりも (わずかに) 効率的であるように見えます。これはおそらく、デバッガーを接続する労力などと関係があります。
- また、IDE の外部でアプリケーションを再起動すると、最悪の場合でも起動時間がさらに短縮されることに注意してください。起動時の 0.3 秒が受け入れられないシナリオもあるかもしれませんが、これが多くの場所で問題になるとは思えません。
- アセンブリの分割に関係なく、IDE 内の初期化と実行時間は安定しています。これは、アセンブリ間でシンボルを解決しやすくするためにデバッグする必要があるという事実のケースである可能性があります。
- IDE の外でも、この安定性は続きますが、注意点が 1 つあります...アセンブリの数は実行には関係ありませんが、継承階層を横断する場合、実行時間は. 違いが小さすぎて体系的ではないことに注意してください。おそらく、同じ最適化を行う方法を理解するためにランタイムが1回かかるのは余分な時間です...率直に言って、これをさらに調査することはできますが、違いは非常に小さいため、あまり心配する必要はありません.
以上のことから、より多くのアセンブリの負担は主に開発者が負担し、そのほとんどがコンパイル時間の形であることがわかります。既に述べたように、これらのプロジェクトは非常に単純で、それぞれのコンパイルに 1 秒もかからず、アセンブリごとのコンパイル オーバーヘッドが支配的でした。多数のアセンブリにわたる 1 秒未満のアセンブリ コンパイルは、これらのアセンブリが妥当な範囲を超えて分割されていることを強く示していると思います。また、プリコンパイルされたアセンブリを使用すると、分割 (コンパイル時間)に対する開発者の主な議論もなくなります。
これらの測定では、実行時のパフォーマンスのために小さなアセンブリに分割することに対する証拠があったとしても、ほとんど確認できません。(ある程度) 注意すべき唯一のことは、可能な限り継承を切断しないようにすることです。継承は通常、機能領域内でのみ発生し、通常は単一のアセンブリ内で終了するため、ほとんどの健全な設計ではこれが制限されると思います。