静的メソッドとインスタンスメソッドのパフォーマンス


108

私の質問は、静的メソッドとインスタンスメソッドのパフォーマンス特性とそのスケーラビリティに関連しています。このシナリオでは、すべてのクラス定義が単一のアセンブリ内にあり、複数の個別のポインタータイプが必要であると想定します。

考慮してください:

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とは対照的に、インスタンスメソッドの解決に少し時間がかかります。

私の質問は:

  1. 状態の保持が問題にならない場合(フィールドやプロパティは不要)、静的クラスを使用する方が常に良いでしょうか?

  2. これらの静的クラス定義が相当数ある場合(たとえば、それぞれが静的メソッドの数が100の場合)、同じ数のインスタンスクラス定義と比較して、実行パフォーマンスまたはメモリ消費に悪影響がありますか?

  3. 同じインスタンスクラス内の別のメソッドが呼び出されても、インスタンス解決は引き続き発生しますか?たとえば、同じインスタンスthis.DoOperation2("abc")内からのように[this]キーワードを使用しDoOperation1ます。



「インスタンス解決」とはどういう意味ですか?ILレベルでは、「this」ポインタは他のローカル変数と同じように使用できます。実際、一部の古いCLR / JITバージョンでは、 'this'に触れない限り、NULLのインスタンスメソッドを呼び出すことができました。すべてのメンバーの呼び出しを確認してください..
quetzalcoatl 2012

> vijaymukhi.com/documents/books/ilbook/chap8.htmおよび「コールインスタンス」対「コール」だけ。前者は 'this'パラメータを期待し、後者は期待しません。
ケツァルコアトル2012

@Quetzalcoatl混乱して申し訳ありませんが、質問は同じインスタンスからメソッドへのより多くのメソッドであり、それがインスタンスをそれ自体に解決する必要があるかどうかです。
バーニーホワイト

1
@quetzalcoatl私は彼が「thisクラスがそれ自体でインスタンスメソッドを呼び出すときに、コンパイラが何かを指すチェックを取り除くのか?」
Jon Hanna

回答:


152

理論的には、静的メソッドはインスタンスメソッドよりもわずかに優れたパフォーマンスを発揮するはずですが、その他のすべての要素は、追加の隠しthisパラメーターがあるため同じです。

実際には、これはほとんど違いがなく、さまざまなコンパイラー決定のノイズに隠されます。(したがって、2人は一方を他方よりも「証明」することができ、反対の結果になります)。特に、this通常はレジスタで渡され、最初はそのレジスタにあることが多いためです。

この最後の点は、理論的には、オブジェクトをパラメーターとして受け取り、それを使って何かを行う静的メソッドが、同じオブジェクトのインスタンスとしての同等のメソッドよりもわずかに劣ることを期待する必要があることを意味します。繰り返しますが、違いはごくわずかなので、測定しようとすると、おそらく他のコンパイラの決定を測定することになります。(特に、その参照がレジスタにある可能性が非常に高いため)。

実際のパフォーマンスの違いは、自然に静的である必要があることを行うためにメモリ内のオブジェクトを人工的に取得したか、またはインスタンスであるはずのことを行うために複雑な方法でオブジェクトの受け渡しのチェーンを巻き込んでいるかどうかによって決まります。

したがって、1番の場合です。状態を維持することが重要でない場合は、静的であることが常に望ましいため、静的であることが常に優れています。これはパフォーマンスの問題ではありませんが、コンパイラの最適化をうまく利用するという一般的なルールがあります。通常の使用で発生するケースを奇妙な使用で発生するケースよりも最適化する努力をした人がいる可能性が高いです。

番号2。違いはありません。各メンバーには、メタデータの量、実際のDLLまたはEXEファイルに含まれるコードの量、およびjittedコードの量の両方について、クラスごとのコストがいくらかあります。これは、インスタンスであっても静的であっても同じです。

項目3ではthisthisそうです。ただし注意:

  1. thisパラメータは、特定のレジスタに渡されます。同じクラス内でインスタンスメソッドを呼び出す場合、それはおそらくそのレジスタにある(それが格納され、レジスタが何らかの理由で使用されない限り)のでthis、に設定する必要があるものにを設定するために必要なアクションはありません。。これは、ある程度、たとえばメソッドの最初の2つのパラメーターが、呼び出しの最初の2つのパラメーターであるメソッドに適用されます。

  2. thisがnullではないことは明らかであるため、これを使用して呼び出しを最適化する場合があります。

  3. thisnullではないことは明らかなので、インライン化されたメソッド呼び出しが再び効率的になる可能性があります。メソッド呼び出しを偽造するために生成されたコードは、とにかく必要なnullチェックを省略できるためです。

  4. とはいえ、ヌルチェックは安価です!

インスタンスメソッドではなくオブジェクトに作用する一般的な静的メソッドを使用すると、http://joeduffyblog.com/2011/10/23/on-generics-and-some-of-で説明されているコストの一部を削減できることに注意してください。与えられたstaticが与えられた型に対して呼び出されない場合のthe-associated-overheads / 「余談ですが、拡張メソッドは一般的な抽象化をより効果的にするための優れた方法であることがわかります。」

ただし、これはメソッドによって使用される他のタイプのインスタンス化にのみ関連し、他の方法では存在しないことに注意してください。そのため、実際には多くの場合には適用されません(他のインスタンスメソッドがその型を使用し、他のコードが他の場所でその型を使用)。

概要:

  1. ほとんどの場合、インスタンスと静的のパフォーマンスコストはごくわずかです。
  2. たとえば、静的を乱用する場合、またはその逆の場合、一般的にどのようなコストがかかります。staticとinstanceの間の決定の一部にしないと、正しい結果が得られる可能性が高くなります。
  3. 別のタイプの静的なジェネリックメソッドを使用すると、インスタンスジェネリックメソッドよりも作成されるタイプが少なくなることがまれにあります。そのため、めったに使用されないようにするメリットが小さい場合あります(また、「まれに」、呼び出される頻度ではなく、アプリケーションの寿命。彼がその記事で話していることを理解したら、それはとにかく、ほとんどの静的対インスタンスの決定には100%無関係であることがわかります。編集:そして、それはほとんどjgenされたコードではなく、ngenでのみそのコストを持っています。

編集:(私が上で主張した)ヌルチェックがいかに安いかについてのメモ。.NETのほとんどのnullチェックは、nullをまったくチェックせず、正常に機能するという前提で処理を続行し、アクセス例外が発生するとに変わりNullReferenceExceptionます。そのため、主に概念的にC#コードがインスタンスメンバーにアクセスしているためにnullチェックが含まれる場合、成功した場合のコストは実際にはゼロです。例外は、いくつかのインライン化された呼び出しであり(インスタンスメンバーを呼び出したかのように動作するため)、フィールドをヒットするだけで同じ動作をトリガーするため、非常に安価であり、いずれにしても除外されることがよくあります。 (たとえば、メソッドの最初のステップに、フィールドへのアクセスが含まれていた場合)。


静的とインスタンスの質問がキャッシュの一貫性に影響するかどうかについてコメントできますか?どちらかに依存すると、キャッシュミスが発生する可能性が高くなりますか?理由を説明する良い概要はありますか?
scriptocalypse 2015年

@scriptocalypseそうではありません。命令キャッシュは違いを認識せず、そのレベルではthis、明示的なパラメーターを介した、または明示的なパラメーターを介したデータへのアクセスに大きな違いはありません。ここでのより大きな影響は、データと関連データ(値型フィールドまたは配列値が参照型フィールドのデータよりも近い)とアクセスのパターンとの距離です。
Jon Hanna

「理論的には、オブジェクトをパラメーターとして受け取り、それを使って何かを行う静的メソッドは、同じオブジェクトのインスタンスとしての同等のメソッドよりもわずかに劣ると想定する必要があります。」-上記のサンプルメソッドが文字列ではなくオブジェクトとしてのパラメータ、非静的の方が良いですか?たとえば、静的メソッドでオブジェクトをパラメーターとして受け取り、それを文字列にシリアル化して文字列を返します。この場合、非静的を使用することを提案していますか?
batmaci

1
@batmaci可能性obj.DoSomehting(2)はわずかに安い可能性があることを意味しますがDoSomething(obj, 2)、違いはごくわずかであり、最終的なコンパイルで最終的に異なる可能性がある小さなことに依存しているので、まったく心配する価値はありません。何かを文字列にシリアル化するのと同じくらい高価なもの(ここでの遊びの種類の違いに比べて)をしている場合、それは特に無意味です。
Jon Hanna

この他の点では優れた答えには、おそらく明白ではあるが重要なものが欠けています。インスタンスメソッドにはインスタンスが必要であり、インスタンスの作成は安くはありません。デフォルトでctorも、すべてのフィールドの初期化が必要です。インスタンスが既にあると、この答えが適用されます(「他のすべてのものは等しい」)。もちろん、コストcctorがかかると静的メソッドも遅くなる可能性がありますが、それは最初の呼び出しでのみインスタンスメソッドに等しく適用されます。docs.microsoft.com/en-us/previous-versions/dotnet/articles/…
Abel

8

状態の保持が問題にならない場合(フィールドやプロパティは不要)、静的クラスを使用する方が常に良いでしょうか?

そうですね。何かstaticを宣言するときに、ステートレス実行の意図を宣言します(これは必須ではありませんが、期待される何かの意図)

これらの静的クラスの数が多い場合(たとえば、100で、それぞれに静的メソッドの数がある場合)、同じ数のインスタンスクラスと比較して、実行パフォーマンスやメモリ消費に悪影響がありますか?

静的クラスが本当に無意味であることが確実でない限り、そうではないと思います。そうでない場合は、メモリ割り当てをめちゃくちゃにして、メモリリークを発生させます。

[this]キーワードを使用して同じインスタンスクラス内の別のメソッドを呼び出す場合、インスタンスの解決は引き続き発生しますか?

この点についてはわかりません(これはCLRの純粋な実装の詳細です)。


静的メソッドをモックすることはできません。TDDを実行する場合や、単体テストを実行する場合でも、これはテストに大きな影響を与えます。
trampster

@trampsterなんで?それは単なる論理の一部です。与えたものを簡単にあざけることができますか?正しい動作を得るため。とにかく、静的メソッドの多くは、とにかく関数内のプライベートロジックです。
M. Mimpen

@ M.Mimpenは、それを小さなプライベートな部分に細かく任せる限り、パブリックメソッドで他のクローズから使用し、テストでの動作を変更する必要がある場合は、ファイルIOやデータベースアクセスなどをスタックします。またはネットワーク呼び出しなど、静的メソッドに配置した場合、モック可能依存関係を静的メソッドへのパラメーターとして注入したと言わない限り、モック不可能になります
trampster

-2

静的メソッドは高速ですが、OOPが少なくなります。デザインパターンの静的メソッドを使用する可能性がある場合は、不正なコードを使用して、静的でなくビジネスロジックをより適切に記述します。回答


16
あなたはあなたの主張に議論を与えませんでした。
ymajoros 2012

2
@ fjch1997 2人の賛成者が別の方法で考えているようです(その価値について)downvotesをコメントすることはstackexchangeに奨励練習です:meta.stackexchange.com/questions/135/...
ymajoros
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.