クラスに複数のインターフェイスを実装させるパフォーマンスについて質問がありました。クラスに2つのインターフェイスと10のインターフェイスを実装させることで劣化はありますか?
背景
この質問は、MicrosoftのIdentity2を調査することから生じます。 ClaimsIdentityFactory.CreateIdentity()
の呼び出し中に〜6回のレプリケートされたDB呼び出しがあることに気付きました。さらに調査したところ、SecurityStamp、Roles、およびその他の情報を確認するときに、コードがユーザーのIDを複数回取得しているように見えました。
これは主に、UserStore
がISecurityStore
またはTUser
モデルに関連付けられるのではなく、IUser
を実装することによってSecurityStampを処理することになっているためです。理由は理解できますが、TUser
がISecurityUser
を実装しているかどうかを確認し、そのデータを取得するだけでは不十分でした。特に、数行前にTUser
を取得したばかりの場合。
それほど大きな問題ではないかもしれませんが、10秒ごとに更新のためにAPIを取得するJSスクリプトがあるので、それは役に立ちます。私が実行しているサーバーは多くの異なるプログラムを実行しているためそれほど強力ではないため、プログラムが冗長なDB呼び出しを待機し、貴重なCPU時間を解放する回数を減らしようとしています。現在、最大で10回のDB呼び出しで呼び出しが発生するまで、約0.02秒かかります。しかし、冗長だと思うDB呼び出しが5つあり、削除するとコードのスループットがさらに向上する可能性があります。
インターフェイスを実装するだけでは、パフォーマンスに影響はありません。
インターフェイスタイプを介してメソッドを呼び出すことは、クラスタイプを介してメソッドを呼び出すよりも少し遅い場合がありますが、MicrosoftのCLRなどの最新のランタイムでは、ほとんどの場合、余分なオーバーヘッドを排除できます。 (彼らは インラインキャッシュ と呼ばれる最適化手法を使用します。これはCLRが仮想スタブディスパッチと呼びます。)
同じデータを繰り返し計算することは、インターフェースとは何の関係もなく、インターフェースの効率とはさらに関係がありません。これは無関係な設計上の問題です。
しかし、インターフェースを介した抽象化が、設計上の問題の適切な解決策であるとは限らないことは事実です。概念的には、インターフェースを使用すると、実際のオブジェクトに関する情報が消去されます。複数のオブジェクトが同じインターフェースを実装している場合、それらを交換可能に使用できるため、これは時々有益です。一方、これらのオブジェクトを異なる方法で処理することはより困難になります。追加機能についてオブジェクトを照会します。オブジェクトを別のインターフェースにキャストすることは、そのような関数の呼び出し元にとっては明白ではなく、あまり拡張性がないため、避けられます。ジェネリックはインターフェースよりも優れたソリューションである場合もありますが、質問のコンテキストでは利点がないようです。
いくつかの問題はうまく解決するのが難しいので、しっかりしているが効率の悪い解決策を選ぶのは正当なようです。
短い答え:no
。コンパイラーがインライン化に失敗した場合でも、クラスがより多くのインターフェースを実装するため、動的ディスパッチのコストは増加しません。動的ディスパッチにはコストがかかりますが、クラスが1つのインターフェイスを実装するか100を実装するかに関係なく、これまでに見た実装ではアルゴリズム的にスケーリングされません。
そうは言っても、あなたがデザイン関連の問題を抱えていると思うので、私は売り込みました。パフォーマンスが懸念される場合は、実際には、デザインの一般的な優先順位を逆にして、最初にデータ指向の考え方を優先する必要があると思います。まず、コードがどのようにデータを表現し、アクセスし、操作するかを決定することから始まります。インターフェースは2番目です。これは、データを取得して操作するだけで、見苦しい実装の詳細を公開する、まったく逆の貧弱なインターフェイスを設計することを意味するものではありません。代わりに、データを保存、アクセス、および操作する最も効率的な方法を考慮しながら、適切な粗さまたは粒度でモデル化することを意味します。
効率を上げるために、多くの場合、きめ細かい方法ではなく、よりかさばる方法で考える必要があります(Users
、User
、Image
、Pixel
ではありません)。多くの場合、最も小さくて単純なオブジェクトをモデル化するには、システムのインターフェイスにカスケードや破壊的な変更を加えずに、それ以上最適化できないコーナーに自分自身をペイントする必要があります。
基本的な例として、品質の大きな側面として効率に本当に関心のあるパーティクルシステムは、数千人に直接公開および操作されるハンドル以上の役割を果たすスカラーParticle
オブジェクトを必ずしも公開するわけではありません。複雑なコードベースの異種クライアント。これにより、コードを一元的に最適化して、単一のパーティクルの詳細レベルまで、詳細なParticle
オブジェクトへの無数の依存関係を持つ機能が制限されます。シングルスレッドのスカラーコードを使用してすべてのクライアントが一度に1つのパーティクルで作業している場合は、複数のパーティクルを同時に並行して実行したり、ベクトル化したりできる中央最適化を優先することはできません。たとえば、個々のパーティクルのフィールドをスカラーParticle
オブジェクト内に格納する必要がある場合、効率的なSoA表現のために、複数のパーティクルのデータフィールドを一度に合体してインターリーブすることはできません。一度に1つのAoSパーティクルを操作することに限定され、そのような設計のパディングやオブジェクトごとの非表示データフィールドなどによるメモリの浪費という非効率的な領域に閉じ込められています。そのため、このようなシステムでは、スカラーParticleSystem
インターフェイスを使用して一度に1つのパーティクルではなく、一度に多数のパーティクルを操作する関数を備えたParticle
インターフェイスを公開する場合があります。
あなたの場合も同様です。多くの冗長なデータベースクエリを排除したい場合、設計の出発点は、最小限のクエリを実行するような方法でデータを効率的に表現、アクセス、保存、および操作する方法である必要があります。私は実際にこれを最初は中央の場所からまとめて行うことをお勧めします。インターフェースは2番目です。インターフェースが最初に来ると、多くの場合、インターフェースや高レベルの面で調整されている可能性のある非常に多くの詳細なクラスが存在する結果として、データが表現、アクセス、および/または非効率的に操作されるケースが発生します。設計しますが、データアクセスとストレージパターンの観点からではありません。
効率が主要な目標でない場合は、それで問題ありません。効率が問題にならないような場合は、計算コストに関係なく機能するために必要なことを、きめ細かいインターフェイスの背後にある実装にいつでも実行させることができます。しかし、それが大きな懸念事項である場合は、データ指向の考え方で設計の問題に取り組む必要があります。そうすれば、その過度にきめ細かい非効率の罠に陥ることはありません。