メソッドの引数を渡すために C# で params を使用するためのアドバイスはありますか? 最初の 6 つの引数のオーバーロードを作成し、次に params 機能を使用して 7 番目の引数を作成することを検討しています。私の理由は、params 機能が必要とする余分な配列の割り当てを避けることです。これは、いくつかの高パフォーマンスのユーティリティ メソッド用です。何かアドバイス?すべてのオーバーロードを作成するのはコードの無駄ですか?
6 に答える
正直なところ、「時期尚早の最適化!」と叫んでいる皆さんに少し困っています。理由は次のとおりです。
- 特に、高性能ライブラリに取り組んでいることをすでに示しているので、あなたの言うことは完全に理にかなっています。
- BCL クラスでさえ、このパターンに従います。
string.Format
orのすべてのオーバーロードを考慮してくださいConsole.WriteLine
。 - これは非常に簡単です。時期尚早の最適化に反対する動きの背後にある全体的な前提は、パフォーマンスを最適化する目的でトリッキーなことをすると、偶然に何かを壊してしまい、コードの保守性が低下する可能性があるということです。ここでそれがどのように危険なのかわかりません。あなた自身だけでなく、あなたのコードを扱う将来の開発者にとっても、あなたがやっていることは非常に簡単であるべきです。
また、両方のアプローチの結果をプロファイリングし、速度の差が非常に小さい場合でも、メモリ割り当ての問題は依然として存在します。メソッド呼び出しごとに新しい配列を作成すると、後でガベージ コレクションが必要になるメモリをさらに割り当てる必要があります。また、「ほぼ」リアルタイムの動作が必要なシナリオ (アルゴリズム取引、私がいる分野など) では、ガベージ コレクションを最小限に抑えることは、実行速度を最大化することと同じくらい重要です。
ですから、たとえそれが私にいくつかの反対票を獲得したとしても:私はそれをやると言います.
(そして、「コンパイラは確かにすでにこのようなことをしている」と主張する人には、私は確信が持てません。まず、そうであれば、なぜBCLクラスがこのパターンに従うのかわかりません.すでに述べました. しかしもっと重要なことに,複数の引数を受け入れるメソッドと配列を受け入れるメソッドの間には, 非常に大きな意味上の違いがあります. 一方を他方の代わりとして使用できるからといって, コンパイラがそうする, またはすべきであるとは限りません. 、そのような置換を試みます)。
はい、それが .NET フレームワークが使用する戦略です。String.Concat() が良い例です。最大 4 つの文字列のオーバーロードに加えて、params string[] を取るフォールバック オーバーロードがあります。ここで非常に重要なことは、Concat は高速である必要があり、ユーザーが StringBuilder の代わりに + 演算子を使用するときに成功の落とし穴に陥るのを助けるために存在することです。
あなたが得るコードの重複は価格です。それらをプロファイリングして、高速化がメンテナンスの頭痛の種に値するかどうかを確認します。
Fwiw: .NET フレームワークには、このようなマイクロ最適化がたくさんあります。設計者はクラスがどのように使用されるかを実際に予測できなかったため、ある程度必要でした。String.Concat() は、たとえば起動時に 1 回だけ実行される構成リーダーと同様に、プログラムのパフォーマンスにとって重要なタイトな内部ループで使用される可能性があります。あなた自身のコードのエンドユーザーとして、あなたは通常、それについて心配する必要がないという贅沢を持っています。その逆もまた真であり、.NET フレームワーク コードは、マイクロ最適化の利点が測定可能である可能性が低い場合に、マイクロ最適化を大幅に排除しています。とにかくコアコードが遅いときにオーバーロードを提供するようなものです。
常にパラメーターとして渡すことができTuple
ます。パラメーターの型が常に同じ場合は、IList<T>
.
他の回答やコメントが言っているように、次の後にのみ最適化する必要があります。
- 正しい動作を保証します。
- 最適化の必要性の判断。
私のポイントは、メソッドが無制限の数のパラメーターを取得できる場合、その内部のロジックは配列スタイルで機能するということです。したがって、限られた数のパラメーターにオーバーロードを設定しても役に立ちません。限られた数のパラメーターをまったく異なる方法で実装できる場合を除き、はるかに高速です。
たとえば、パラメータを Console.WriteLine に渡す場合、そこにも隠し配列が作成されるため、どちらの方法でも配列が作成されます。
また、Dan Tao さんにお手数をおかけして申し訳ありません。最適化が時期尚早のようにも感じます。パラメーターの数が制限されたオーバーロードを使用すると、どのような違いが生じるかを知る必要があるためです。アプリケーションのパフォーマンスが非常に重要な場合は、両方の方法を実装し、テストを実行して実行時間を比較する必要があります。
このようなことを試してパフォーマンスをベンチマークすると、決定を下すための具体的な数値が得られます。
一般に、オブジェクトの割り当ては C/C++ よりもわずかに高速であり、小さなオブジェクトの削除ははるかに高速です (1 秒間に数万個のオブジェクトが作成されるまで)。これは、メモリ割り当てのパフォーマンスに関する古い記事です。
この段階では、パフォーマンスについても考えないでください。今から 2 年後の午前 4 時にコードを書きやすく理解しやすくするオーバーロードを作成します。それはパラメーターを意味する場合もあれば、回避することを意味する場合もあります。
動作するものが得られたら、これらがパフォーマンスの問題であるかどうかを判断します。パラメータをより複雑にすることは難しくありませんが、今不要な複雑さを追加すると、後でそれほど複雑になることはありません。