最近、最適化は失われた芸術のようです。すべてのプログラマーが自分のコードからあらゆるオンスの効率を絞り出した時代はありませんでしたか?雪の中を5マイル歩いている間によくそうしますか?
失われた芸術を取り戻すという精神で、C#/。NETコードを最適化するための単純な(またはおそらく複雑な)変更について知っているヒントは何ですか?何を達成しようとしているのかに依存するのは非常に広いので、コンテキストをヒントに提供するのに役立ちます。例えば:
StringBuilder
を使用します。これに関する警告については、下部のリンクを参照してください。string1.ToLower() == string2.ToLower()
のようなことをする代わりに、string.Compare
を使用して2つの文字列を比較しますこれまでのところ、一般的なコンセンサスが測定されているようです。この種の点を見逃しています。測定では、何が間違っているのか、ボトルネックに陥った場合に何をすべきかがわかりません。一度文字列連結のボトルネックに遭遇しましたが、どうすればよいのかわからなかったので、これらのヒントは役に立ちます。
これを投稿することに対する私のポイントは、一般的なボトルネックが発生する前に、それらを回避する方法を用意することです。誰もが盲目的に従うべきプラグアンドプレイコードである必要はありませんが、パフォーマンスを少なくともある程度考慮する必要があり、注意すべき一般的な落とし穴があることを理解することに関する詳細です。
しかし、ヒントが有用である理由と適用する場所を知ることも役立つかもしれません。 StringBuilder
のヒントについては、ずっと前に ここではJon Skeetのサイトにあります で助けを見つけました。
最近、最適化は失われた芸術のようです。
たとえば、顕微鏡の製造が芸術として実践されたのは1日1回でした。光学原理はよく理解されていませんでした。部品の標準化はありませんでした。チューブ、ギア、レンズは、熟練した労働者が手作業で作成する必要がありました。
最近の顕微鏡は、工学分野として生産されています。物理学の根底にある原理は非常によく理解されており、既製の部品が広く利用可能であり、顕微鏡製造エンジニアは、実行するように設計されたタスクに対して機器を最適化する方法について十分な情報に基づいた選択を行うことができます。
パフォーマンス分析は「失われた芸術」であるということは、非常に良いことです。その芸術は芸術として実践された。最適化は、それが何であるかについてアプローチする必要があります:エンジニアリングの問題堅実なエンジニアリングの原則を慎重に適用することで解決可能です。
私は、人々がvbscript/jscript /アクティブサーバーページ/ VB// C#を最適化するために使用できる「ヒントとコツ」のリストを何十回も求められてきました。 「ヒントとテクニック」を強調することは、パフォーマンスにアプローチするためのまったく間違った方法です。その方法は、理解しにくく、推論しにくく、維持しにくいコードにつながります。通常、対応する単純なコードよりも顕著に高速ではありません。
パフォーマンスにアプローチする正しい方法は、他の問題と同様に、エンジニアリングの問題としてアプローチすることです。
これは、機能の追加など、他のエンジニアリングの問題を解決するのと同じです-機能に顧客中心の目標を設定し、堅実な実装の進行状況を追跡し、慎重なデバッグ分析で見つかった問題を修正し、繰り返されるまで繰り返します出荷または失敗します。 パフォーマンスは機能です。
複雑で現代的なシステムのパフォーマンス分析には、些細な、または非現実的な状況に狭く適用できるトリックでいっぱいのバッグではなく、しっかりしたエンジニアリングの原則に規律と焦点を合わせる必要があります。ヒントやコツを使って実際のパフォーマンスの問題を解決したことは一度もありません。
優れたプロファイラーを入手してください。
優れたプロファイラーを使用せずにC#(実際には、任意のコード)を最適化しようとしても気にしないでください。実際には、サンプリングプロファイラとトレースプロファイラの両方を手元に置いておくと劇的に役立ちます。
優れたプロファイラーがなければ、誤った最適化を作成する可能性が高く、最も重要なのは、そもそもパフォーマンスの問題ではないルーチンを最適化することです。
プロファイリングの最初の3つのステップは、常に1)測定、2)測定、そして3)測定です。
最適化のガイドライン:
プロセッサの高速化が進むにつれて、ほとんどのアプリケーションの主なボトルネックはCPUではなく、帯域幅です。オフチップメモリへの帯域幅、ディスクへの帯域幅、ネットへの帯域幅です。
遠端から始めましょう:YSlowを使用して、Webサイトがエンドユーザーにとって遅い理由を確認し、戻ってデータベースアクセスを広すぎず(列)、深すぎない(行)に修正します。
CPU使用量を最適化するために何かを行う価値がある非常にまれなケースでは、メモリ使用量に悪影響を与えないように注意してください。開発者がCPUサイクルを節約するためにメモリを使用して結果をキャッシュしようとする「最適化」を見てきました。最終的な効果は、ページとデータベースの結果をキャッシュするために使用可能なメモリを削減し、アプリケーションの実行を大幅に遅くしたことです! (測定に関する規則を参照してください。)
また、「ダム」の最適化されていないアルゴリズムが「賢い」最適化アルゴリズムを破った場合も見ました。コンパイラライターやチップ設計者が、「非効率的な」ループコードを、パイプライン処理を使用してオンチップメモリで完全に実行できる超効率的なコードに変換することで、いかに優れているかを過小評価しないでください。実行中にオンチップメモリにとどまることができなかったという理由だけで、「効率的」と思われる逆方向にカウントするアンラップされた内部ループを使用した「賢い」ツリーベースのアルゴリズムを破ることができます。 (測定に関する規則を参照してください。)
ORMを使用する場合、N + 1の選択に注意してください。
List<Order> _orders = _repository.GetOrders(DateTime.Now);
foreach(var order in _orders)
{
Print(order.Customer.Name);
}
顧客が熱心にロードされていない場合、データベースへのラウンドトリップが何度か発生する可能性があります。
メソッドをボトルネックとして特定しても、それに対して何をすべきかわからない場合、本質的に行き詰まっています。
そこで、いくつかのことをリストします。これらはすべて特効薬ではなく、プロファイルコードが必要です。私はあなたがすることができたすることができ、時々助けることができるもののための提案をしています。特に最初の3つは重要です。
OK、私のお気に入りをスローする必要があります。タスクが人間の対話に十分な長さである場合は、デバッガーで手動ブレークを使用します。
対プロファイラー、これはあなたに何が起こっているのかを本当に理解するために使用できるコールスタックと変数値を提供します。
これを10〜20回行うと、どの最適化が実際に違いを生む可能性があるかがわかります。
人々は実際に重要なことについて面白いアイデアを持っています。 Stack Overflowには、たとえば++i
より多くの「パフォーマンス」i++
。 実際のパフォーマンスチューニングの例 であり、基本的にどの言語でも同じ手順です。コードが単に「高速だから」という特定の方法で書かれている場合、それは推測です。
もちろん、あなたは意図的に愚かなコードを書くことはしませんが、推測がうまくいけば、プロファイラーやプロファイリング技術は必要ありません。
真実は、完全に最適化されたコードなどはありません。ただし、既知のCPUタイプ(およびカウント)の既知のシステム(またはシステムのセット)で、特定の部分のコードに対して最適化できます。 、既知のプラットフォーム(Microsoft? Mono ?)、既知のフレームワーク/ [〜#〜] bcl [〜#〜] バージョン、既知のCLIバージョン、既知のコンパイラバージョン(バグ、仕様変更、調整)、既知の合計および利用可能なメモリ量、既知のアセンブリの起源( [〜#〜] gac [〜#〜] ?disk?remote?)他のプロセスからの既知のバックグラウンドシステムアクティビティ。
現実の世界では、プロファイラーを使用して、重要な部分を見てください。通常、明らかなものはI/Oを含むもの、スレッド化を含むもの(これもバージョン間で大きく変化します)、ループとルックアップを含むものですが、「明らかに悪い」コードが実際問題ではないことに驚くかもしれません。そして、「明らかに良い」コードとは、大きな犯人です。
コンパイラにwhatを行うよう指示し、howを行うように指示しないでください。例として、foreach (var item in list)
はfor (int i = 0; i < list.Count; i++)
よりも優れており、m = list.Max(i => i.value);
はlist.Sort(i => i.value); m = list[list.Count - 1];
よりも優れています。
何をしたいのかをシステムに伝えることにより、それを行うための最良の方法を見つけることができます。 LINQは、必要になるまで結果が計算されないため、優れています。最初の結果のみを使用する場合、残りを計算する必要はありません。
最終的に(そしてこれはすべてのプログラミングに当てはまります)、ループを最小化し、ループで行うことを最小化します。さらに重要なのは、ループ内のループの数を最小限にすることです。 O(n)アルゴリズムとO(n ^ 2)アルゴリズムの違いは何ですか?O(n ^ 2)アルゴリズムにはループの内側にループがあります。
私は実際にコードを最適化しようとはしていませんが、時にはリフレクターのようなものを使ってプログラムをソースに戻します。それから、私が間違っていることと、リフレクターが出力することを比較することは興味深いです。時々、もっと複雑な形でやったことが単純化されたことがわかります。物事を最適化することはできませんが、問題に対するより簡単な解決策を見つけるのに役立ちます。