私の質問は、静的メソッドとインスタンス メソッドのパフォーマンス特性、およびそれらのスケーラビリティに関するものです。このシナリオでは、すべてのクラス定義が単一のアセンブリ内にあり、複数の個別のポインター型が必要であると仮定します。
考慮する:
public sealed class InstanceClass
{
public int DoOperation1(string input)
{
// Some operation.
}
public int DoOperation2(string input)
{
// Some operation.
}
// … more instance methods.
}
public static class StaticClass
{
public static int DoOperation1(string input)
{
// Some operation.
}
public static int DoOperation2(string input)
{
// Some operation.
}
// … more static methods.
}
上記のクラスはヘルパー スタイルのパターンを表します。
インスタンス クラスでは、StaticClass とは異なり、インスタンス メソッドの解決に少し時間がかかります。
私の質問は次のとおりです:
状態を維持する必要がない場合 (フィールドやプロパティは不要)、常に静的クラスを使用する方が良いのでしょうか?
このような静的クラス定義が相当な数ある場合 (たとえば、それぞれに多数の静的メソッドがある 100 個など)、同じ数のインスタンス クラス定義と比較して、実行パフォーマンスやメモリ消費に悪影響が出るでしょうか?
同じインスタンス クラス内の別のメソッドが呼び出された場合、インスタンス解決は依然として行われますか? たとえば、同じインスタンス
this.DoOperation2("abc")
内から[this] キーワードを使用するなどです。DoOperation1
ベストアンサー1
理論的には、追加の隠しパラメータがあるため、他のすべての条件が同じであれば、静的メソッドはインスタンス メソッドよりもわずかに優れたパフォーマンスを発揮するはずですthis
。
実際には、これはほとんど違いをもたらさないため、さまざまなコンパイラの決定のノイズに隠れてしまいます。(したがって、2 人の人が、一方が他方よりも優れていることを「証明」しても、結果は一致しない可能性があります)。特に、 はthis
通常レジスタで渡され、最初からそのレジスタにあることが多いためです。
この最後のポイントは、理論上、オブジェクトをパラメータとして受け取り、それを使って何かを行う静的メソッドは、同じオブジェクトのインスタンスとして同等のものよりも若干劣ると予想されることを意味します。ただし、違いは非常に小さいため、それを測定しようとすると、おそらく他のコンパイラの決定を測定することになります。(特に、その参照が常にレジスタ内にある可能性も非常に高いため)。
実際のパフォーマンスの違いは、本来は静的であるべきことを実行するためにメモリ内に人工的にオブジェクトを配置しているか、本来はインスタンスであるべきことを実行するために複雑な方法でオブジェクト受け渡しのチェーンを絡ませているかによって決まります。
したがって、1番目です。状態を維持することが問題にならない場合は、常に静的である方が良いです。なぜならそれが静電気の目的だからこれはパフォーマンスの問題ではありませんが、コンパイラの最適化をうまく利用するための全体的なルールがあります。つまり、奇妙な使用方法で発生するケースよりも、通常の使用方法で発生するケースを最適化するために誰かが努力した可能性が高くなります。
2 番目。違いはありません。メタデータの量、実際の DLL または EXE ファイル内のコードの量、JIT されたコードの量の両方の観点から、各メンバーのクラスごとに一定のコストがかかります。これは、インスタンスでも静的でも同じです。
項目 3 とthis
同様ですthis
。ただし、次の点に注意してください。
パラメータ
this
は特定のレジスタに渡されます。同じクラス内のインスタンス メソッドを呼び出す場合、そのパラメータは既にそのレジスタにある可能性が高いため (パラメータがスタッシュされ、何らかの理由でそのレジスタが使用されている場合を除く)、パラメータをthis
必要な値に設定するためのアクションは必要ありません。これは、たとえばメソッドの最初の 2 つのパラメータが、メソッドが行う呼び出しの最初の 2 つのパラメータである場合に、ある程度適用されます。が null ではないことは明らかなので
this
、場合によってはこれを使用して呼び出しを最適化することができます。が null でないことは明らかなので
this
、メソッド呼び出しを偽装するために生成されたコードではいずれ必要になる可能性のある null チェックの一部を省略できるため、インライン メソッド呼び出しがさらに効率的になる可能性があります。そうは言っても、null チェックは安価です。
注目すべきは、インスタンスメソッドではなくオブジェクトに作用する汎用静的メソッドは、前述のコストの一部を削減できることである。http://joeduffyblog.com/2011/10/23/on-generics-and-some-of-the-related-overheads/ より特定の静的メソッドが特定の型に対して呼び出されない場合。彼は「余談ですが、拡張メソッドは、ジェネリック抽象化をより効果的なものにする優れた方法であることがわかりました。」と述べています。
ただし、これはメソッドによって使用される他の型のインスタンス化にのみ関係し、それ以外の場合は存在しないことに注意してください。そのため、実際には多くのケースには適用されません (他のインスタンス メソッドがその型を使用したり、他の場所のコードがその型を使用したりします)。
まとめ:
- ほとんどの場合、インスタンスと静的のパフォーマンス コストは無視できるほど小さいです。
- 一般的に、コストが発生するのは、インスタンスを静的に乱用した場合、またはその逆の場合です。静的とインスタンスのどちらを使用するか決定する際に、このことを考慮に入れなければ、正しい結果が得られる可能性が高くなります。
- まれに、別の型の静的ジェネリックメソッドによって、インスタンスジェネリックメソッドよりも少ない型が作成されることがあります。時々めったに使用されないようにする利点はわずかです (「めったに」とは、アプリケーションの存続期間中にどの型で使用されるかを指し、呼び出される頻度ではありません)。この記事で彼が何を言っているのかを理解すれば、ほとんどの静的 vs インスタンスの決定とは 100% 無関係であることがわかります。編集: ほとんどの場合、そのコストは ngen でのみ発生し、jitted コードでは発生しません。
編集: null チェックがいかに安価であるかについてのメモ (上記で主張しました)。.NET のほとんどの null チェックは null をまったくチェックせず、むしろそれが機能するという前提で実行しようとしていたことを続行し、アクセス例外が発生した場合は に変換されますNullReferenceException
。そのため、ほとんどの場合、概念的には C# コードがインスタンス メンバーにアクセスしているために null チェックを含む場合、成功した場合のコストは実際にはゼロです。例外は、一部のインライン呼び出し (インスタンス メンバーを呼び出したかのように動作するため) であり、同じ動作をトリガーするためにフィールドをヒットするだけなので、これも非常に安価であり、とにかく省略できることがよくあります (たとえば、メソッドの最初のステップでフィールドへのアクセスがそのまま含まれている場合)。