初心者のためのC懸垂の最適化(第1部)


あなたがレガシーシステムで働いているとき、いくつかの時間の後にいくつかの問題があるはずです.それは職業上の危険です別の開発者は、プロジェクト、別のアプローチと異なる優先順位をオフに回転している.タスクによっては、通常、解決策の効率は余念であり、時間の経過とともに、いくつかの実行中のクエリーを引き起こす可能性があります.
ゆっくり実行しているクエリに取り組むことは圧倒的でありえます、どこで、あなたも始めますか?それはあなたのデータベースです、それはあなたのコードです、それはネットワークです、それは機械の幽霊です.あなたのプロファイラを見てきたし、どの方法は、時間浪費ですが、今何を教えて?この2つの部分シリーズでは、私はどのように小さなものを変更することはかなりの違いを見ることを目指しています.
私の最適化のポストの一部は、物事のコード側に焦点を当て;「正しい」データ構造、延期された実行とあなたの荷積み戦略を選ぶことによってLINQから最高を得る方法.

データ構造
データ構造は、組織化された方法でデータを保存する方法です.各構造は、特定の機能、主に並べ替え、検索と挿入/削除で効率的であることを目指しています.したがって、右の1つを選択すると、かなりの時間を節約することができます.データ構造はLINQを使用するときに重要です.ほとんどの初心者はIEnumerableやリストを実行しますが、必ずしも最適ではありません.ここでは、データ構造のいくつかの例とどのように最高のパフォーマンスをそれぞれを使用する方法です.

IEnumerable
iEnumerableはほとんどC Cのコレクションで実装されているインターフェイスです.これらは、複数の埋め込まれたLINQクエリを書くときに最適です.
彼らはカウント、maxや平均などの集約関数で公平ではない傾向があります.これは、コレクション内のオブジェクトの数に関する知識がないためです.一般的な間違いは、コレクション全体を反復して各項目をカウントするcount ()メソッドを使用することです.
IEnumerablesも、それがコレクション内の特定の値を見つけることになると、それは価値が最後のものである場合は吸う値を見つけるまで、すべての項目を繰り返している場合は素晴らしいされていません.

リスト
リストは最も一般的なデータ構造のうちの1つです.IEnumerableインターフェイスを実装し、コレクションのサイズに関する知識を持ち、インデックスを持っています.インデックスは特定の場所を見つけるためのキーです、リストはコレクションの特定の位置で値を見つけるためにインデックスを使用します.場合は、リスト内の100番目のアイテムが必要な場合、それは直接アイテムに移動し、それを取得する前に99項目を検索することとは異なり、それを取得します.これはまた、項目のインデックスを提供することによって挿入と削除を非常に容易にします.
リストも、彼らの低下を持ちます、インデックスなしで特定の価値を捜すことができません.あなたが牛乳、卵、ワイン、ブロッコリーと食料品店のリストを持っている場合などの場合は、食料品のリストにワインを追加することを覚えている場合は、例えば、あなたが発見したい.それはすべてのアイテムを反復するので、リストを使用することは理想的ではない、“ワイン”と“ワイン”に到達する前に“卵”と“ワイン”と“ワイン”を比較することを意味します.これは、4つのアイテムを持っている場合は最悪のことではありませんが、レストランのチェーンを所有するとき何が起こるか?リストは明らかに4つ以上の項目になります、それは非常に長いことになりそうです.あなたは1つのアイテムを見つけるためにリスト全体を繰り返しますか?Cでは一般的な初心者の間違いは、それを正確に行うコンテナ()メソッドを使用している、大量のデータを持っているとき、これは遅くなることができます.

辞書
辞書は、キー/値のペアを使用してコレクション内の特定の項目を検索するために最適化されます.これはあなたのインデックスとして機能するキーを選択することによって動作します.キーはハッシュ値を作成して、ハッシュ値でデータを保存するためにハッシュ関数で使用されます.あなたは単語の定義を探している場合は、実際の生活辞書に非常に似ている場合は、あなたが探している単語のインデックスページをチェックし、それはあなたの定義の場所を与える.インデックスなしでは、単語を見つけるまでは、辞書内のすべての単語を見てする必要があります.したがって、ルックアップにおいて、キーは特定の値を見つけるのに用いられます.そして、O(1)があるリストと比較してどのくらいのデータが比較するかに関係なくO(1)ルックアップを与えます.
例えば、私たちが主題のリストを持っているならば、「神学」がこのリストに存在するかどうかチェックしたいです.コンテナ内のcontainer ()メソッドを使用して、リストを辞書に変換し、次に示すように検索します.
Dictionary<string,int> subjectsDictionary = 
subjectsList.ToDictionary(key => key.SubjectName, value => value.SubjectID);
subjectsDictionary.ContainsKey(Theology);
これも注意して使用する必要があります、辞書にリストをシリアル化するコストがあります.変換の値は、複数のルックアップを持っているときに表示されます.

ハセット
hash setはハッシュ関数を探索最適化アルゴリズムとして使用する集合の実装である.セットは、異なるオブジェクトのコレクションである数学的定義と同じです.それは複製がないことを保証するオブジェクトのコレクションです.ハッシュ関数はルックアップでハッシュ関数を使用するため、辞書に非常によく似ていますが、キー/値のペアを使用しないという点で異なります.また、特定の値を見つけるのに最適です.

繰延処刑
繰返された実行のアイデアは、クエリがフィルタリングされたときに必要な詳細についてのみ列挙することです.IEnumerableをリストにシリアル化することは、すぐにあなたの質問に余分な時間を加えることができます.Tolistメソッドを呼び出すと、リスト全体をメモリに格納します.リストが正確に濾過されないならば、不必要なデータは列挙されます.基本的に、あなたの結果を列挙する前に、可能な限り最後まで待つ.
例えば、私たちには2つのクラス、学生と科目があります.LINQを使って、心理学を勉強している25歳以上の生徒の名前を見つけたいです.以下の例を示します.
var psycStudents = students.Where(s => s.SubjectID == 1).ToList();
var psycStudentsOverTwentyFive = psycStudents.Where(s => s.Age > 25).Select(s =>s.StudentName);
上記のコードは動作しますが、問題は適切にフィルタリングする前に列挙します.それは私たちがこれらの学生のサブセットを探しているので、それは不必要なメモリに心理学(subjectid = 1)を勉強しているすべての学生をもたらします.このクエリを書くより良い方法は次のようになります.
var psycStudentsOverTwenty = students.Where(s => s.SubjectID == 1 && s.Age > 25).Select(s => s.StudentName);
この方法では、LINQは必要なデータだけを返し、データセットが大きい場合は特に時間を節約します.これはまた、アイテムを反復する必要があるときに、それはあなたが必要なアイテムだけを繰り返すことが保証されることができます.

ローディング戦略
Entity Framework(EF)には、データの読み込みを処理する3つの方法があります.怠惰で熱心で明示的な読み込みです.このセクションでは、それぞれの戦略がどのような意味でどのようにコードを最適化するために使用するかを簡単に説明します.負荷の戦略はDBContext(データベースとドメインクラス間の中間クラス)レベルで変更されます.例では、コンテキストVarsityContextExttiesを呼び出します.

怠惰な読み込み
これは3つの中で最も一般的です、名前はデータをロードする方法にいくつかの文脈を与えます.あなたが怠惰な人であるならば、あなたは裸の最小限をします.クエリを作成するとき、要求されたエンティティだけが返されます.efはコードで必要とされるデータを取得するので、すべてのエンティティを先頭に読み込む必要はありません.怠惰なローディングは1つの実体がもう一つのタイプの実体のリストにリンクされる1~多くの関係で役に立つようになります.学生と被験者の例と同様に、各学生は多くの科目を持つことができます.すべての学生のために問い合わせなければならないならば、怠惰な積載を可能にすることは我々が学生実体を得るだけであるのを確実とします、そして、若干の時間を節約することができる主題でない.コードの例は次のようになります.
var students = context.Students.ToList<Student>()
怠惰な読み込みを伴う問題は、それが質問が親実体(学生)のためになされるとき、それが各々の子実体(被験者)のために作られるN + 1質問問題につながるということです.それで、すべての実体を得るために1つの呼び出しをする代わりに、n + 1質問は(学生のための主題を検索するためのNと学生のための初期の呼び出しのための1)されます.これは、パフォーマンスに最適でないDBに複数の呼び出しを行います.

熱心な装填
データベースにクエリを作成するときは、関連するすべてのエントリと共に要求されたエンティティが返されます.たとえば、学生のエンティティは、科目や能力にリンクされている場合.熱心なローディングが有効になっている場合、学生のためのクエリが作成されると、両方の科目と教員のエンティティが取得されます.両方の実体が必要であるなら、これは役に立つでしょう、それはデータベースにされる呼び出しの数を減らします.熱心な読み込みを実行する方法はinclude ()メソッドを使用しています.ここでは、熱心な読み込みを使用してそれぞれの科目や能力を積んでいる学生の例を示します.
using (var context = new VarsityContextEntities())
{
    var students = context.Students.Include("Subject.Faculty")
                    .FirstOrDefault<Student>();
}
あなたが必要とするすべての実体がちょうどちょうど1つの質問にロードされるので、熱心なローディングはN + 1問題の解決でもあります.

明示的負荷
明示的な読み込みは、要求されたデータを返すだけで、遅延の読み込みに似ていますが、明示的に特定のエンティティを要求できます.あなたはEFにどのエンティティをロードするかを教えます.例えば、私たちが学生を望むが、後でどこかスコープの中で、我々は主題実体を使いたいです.データベースへの2回の呼び出しの代わりに、学生を検索するための最初と2番目の呼び出しは主題を検索するために、あなたは1つの呼び出しをするだけです.上記の例を使用してload ()メソッドを使用して明示的な読み込みを行います.
using (var context = new VarsityContextEntities())
{
    var students = context.Students.FirstOrDefault<Student>();
    context.Entry(student).Collection(s => s.Subject).Load(); 
}
初心者として、Cはあなたのかなりの最適化トラップを提供します.単にドット(.)を押します.そして、あなたは即座に選択のオプションのリストと砲撃されます.これは非常にだけでは、その結果の認識されることなく、必要に応じて最も近い音を選択することです.うまくいけば、このポストは、あなたにあなたのコーディングでもう少し意識的で意図的になります.