9

LINQを使用しているコードのセクションでわずかなパフォーマンスの問題が発生し、ルックアップに関してLINQがどのように機能するかについて疑問が生じました。

私の質問はこれです(私はすべてのコードを変更したので、これは実際のシナリオではなく、コードの例示的な例であることに注意してください):

与えられた

public class Person {
 int ID;
 string Name;
 DateTime Birthday; 
 int OrganisationID;
}

たとえば100kのPersonオブジェクトのリストと、たとえば1000の日付のリストがあり、次のコードを実行した場合:

var personBirthdays = from Person p in personList
    where p.OrganisationID = 123
    select p.Birthday;

foreach (DateTime d in dateList)
{
    if (personBirthdays.Contains(d))
        Console.WriteLine(string.Format("Date: {0} has a Birthday", d.ToShortDateString()));
}

結果のコードは、次の反復になります。

100k(organisationID 123のユーザーを見つけるために実行する必要のあるループ)
×
1000(リスト内の日付の量) ×x(日付についてチェックされるorganisationID 123を持つユーザーの数

)。

これはたくさんの繰り返しです!

コードをpersonBirthdaysに変更した場合:

List<DateTime> personBirthdays = 
        (from Person p in personList
        where p.OrganisationID = 123
        select p.Birthday).ToList();

これにより、100kが倍数として削除され、1回だけ実行されますか?

したがって、(100k * 1000 * x)の代わりに100k +(1000 * x)になります。

問題は、これは簡単すぎるように思われることです。LINQは、これが起こらないことを意味するはずの巧妙なことをどこかで行っていると確信しています。

誰も答えない場合は、いくつかのテストを実行して報告します。

明確性の更新: データベースルックアップは考慮していませんpersonList。オブジェクトはメモリ内リストオブジェクトです。これはすべてLINQ-to-Objectsです。

4

3 に答える 3

8

これにより、10kが倍数として削除され、1回だけ実行されますか?

つまり、100k回反復する代わりにpersonList、結果の100k回反復する各反復に対してwhereandselect操作を実行し、 and操作は基になるデータソースに対して1回だけ実行されるということです。Listwhereselect

問題は、これは簡単すぎるように思われることです。LINQは、これが起こらないことを意味するはずの巧妙なことをどこかで行っていると確信しています。

いいえ、最初のクエリはLINQを使用して行うべきではないものです。クエリの結果を何度も繰り返す予定がある場合は、クエリの結果をデータ構造に配置する必要があります(これは変更した内容です)。 。

適切なデータ構造を使用することで、このクエリをさらに改善できます。List線形探索を行う必要があるため、aでの探索はかなり非効率的です。HashSetクエリの結果を保存するには、を使用することをお勧めします。HashSetのO(n)検索時間とは対照的に、Aの平均検索速度はO(1)Listです。

var dates = new HashSet<DateTime>(from Person p in personList
                                  where p.OrganisationID = 123
                                  select p.Birthday);

foreach (DateTime d in dateList.Where(date => dates.Contains(date)))
{
    Console.WriteLine(string.Format("Date: {0} has a Birthday", d.ToShortDateString()));
}
于 2012-12-11T16:00:25.737 に答える
3

これは典型的なselect n+1問題であり、適用した後、.ToList()部分的に解決しました。次のステップは次のようになります。personBirthdaysリストを常に繰り返し、それをに置き換えれば、はるかに高速にHashSet実行して重複を削除できます。Contains(d)

var personBirthdays = new HashSet<DateTime>((from Person p in personList
    where p.OrganisationID = 123
    select p.Birthday).ToArray());
于 2012-12-11T16:01:41.807 に答える
0

各LINQプロバイダーには独自の実装(LINQ-to-SQL、LINQ-to-Entities、LINQ-to-XML、LINQ-to-anything)があるため、LINQ-to-Objectsを参照していると想定しています。

の例をとるpersonBirthdaysと、式が完全な結果セットを反復処理する目的で作成されたというのは当然の結論ではないため、LINQは結果を配列またはリストに自動的に実体化できません。

これらの操作は大きく異なります。

personBirthdays.Distinct()
personBirthdays.FirstOrDefault(b => b.Month == 7)
personBirthdays.Select(b => b.Year).Distinct()

テクノロジーとしてのLINQが行うのは「賢い」ことであり、式ツリーの構築を可能にし、実行を延期することです。これが、上記の3番目の例で誕生日を取得するための100k回の反復、年を選択するためのさらに100k回の反復、そして個別の値を組み立てるための最後のコストのかかるパスを防ぐものです。

LINQの消費者(あなた)は、表現の運命を所有している必要があります。結果セットが複数回繰り返されることがわかっている場合は、それらを配列またはリストに具体化する責任があります。

于 2012-12-11T16:08:00.163 に答える