2

簡単な質問:

どちらが速いですか?

foreach (Object obj in Collection)
{
     if(obj.Mandatory){ ... }
}

また

foreach (Object obj in Collection.FindAll(o => o.Mandatory))
{
...
}

そして、あなたがより速い提案を知っているなら、私は知ってうれしいです。

ありがとうございました

4

6 に答える 6

16

の場合CollectionList<T>FindAll新しいものを作成しList<T>、述語に一致するすべてのアイテムをコピーすることで実装されます。これは、コレクションを列挙し、述語が成り立つかどうかを各アイテムについて決定するよりも明らかに遅いです。

.NET 3.5を使用している場合は、コピーを作成せず、最初の例と同様のLINQを使用できます。

foreach (object obj in someCollection.Where(o => o.Mandatory))
{
    ...
}

これが必ずしも最速のソリューションではないことに注意してください。メモリを割り当ててコレクションを列挙するメソッドは、コレクションのみを列挙するメソッドよりも遅いことが簡単にわかります。パフォーマンスが重要な場合:それを測定します。

于 2009-11-12T17:20:30.673 に答える
7

次のテストコードは、1,000万個のオブジェクトを反復処理するためのシステムティック(1ティック= 100ナノ秒)を出力します。FindAllは最も遅く、forループは予想どおり最も速くなります。

ただし、反復のオーバーヘッドは、最悪の場合でもアイテムあたりのナノ秒単位で測定されます。ループ内で重要なこと(たとえば、アイテムごとにマイクロ秒かかるもの)を実行している場合、反復の速度差はまったく重要ではありません。

したがって、Turingを愛するために、コーディングガイドラインでforeachを禁止しないでください。実用的な違いはなく、LINQステートメントは確かに読みやすくなっています。

   public class Test
   {
      public bool Bool { get; set; }
   }

   class Program
   {

      static void Main(string[] args)
      {
         // fill test list
         var list = new List<Test>();
         for (int i=0; i<1e7; i++)
         {
            list.Add(new Test() { Bool = (i % 2 == 0) });
         }

         // warm-up
         int counter = 0;
         DateTime start = DateTime.Now;
         for (int i = 0; i < list.Count; i++)
         {
            if (list[i].Bool)
            {
               counter++;
            }
         }

         // List.FindAll
         counter = 0;
         start = DateTime.Now;
         foreach (var test in list.FindAll(x => x.Bool))
         {
            counter++;
         }
         Console.WriteLine(DateTime.Now.Ticks - start.Ticks); // prints 7969158

         // IEnumerable.Where
         counter = 0;
          start = DateTime.Now;
         foreach (var test in list.Where(x => x.Bool))
         {
            counter++;
         }
         Console.WriteLine(DateTime.Now.Ticks - start.Ticks); // prints 5156514

         // for loop
         counter = 0;
         start = DateTime.Now;
         for (int i = 0; i < list.Count; i++)
         {
            if (list[i].Bool)
            {
               counter++;
            }
         }
         Console.WriteLine(DateTime.Now.Ticks - start.Ticks); // prints 2968902


      }
于 2009-11-12T17:42:33.390 に答える
6

最初のものはやや速くなります。

2番目のケースでは、を使用List<T>.FindAllして、条件に一致する一時リストを作成しています。これによりリストがコピーされ、それが繰り返されます。

ただし、次の手順を実行することで、最初のオプションと同じ速度で同じことを実行できます。

foreach (Object obj in Collection.Where(o => o.Mandatory))
{
}

これは、Enumerable.Whereがストリーミングを使用してを返すためIEnumerable<T>です。これは反復時に生成されます。コピーは作成されません。

于 2009-11-12T17:21:00.610 に答える
3

プロセッサの数などを考慮して、列挙を複数のスレッドに並列化せずに取得できる最速:

for (int i = 0; i < Collection.Count; i++)
{
    var item = Collection[i];
    if (item.Mandatory) { ... }
}

for書き込みやループの代わりに常にLinqを使用することをお勧めしますforeach。将来的には非常にインテリジェントになり、実際にプロセッサ間で作業を分散し、ハードウェア固有のものを考慮に入れることができるようになり(PLinqを参照)、最終的にはループを自分で作成した場合よりも高速になります。宣言型プログラミングと命令型プログラミングです。

于 2009-11-12T17:20:43.797 に答える
1

FindAllは単なる構文糖衣です。例えば:

    List<string> myStrings = new List<string>();
    foreach (string str in myStrings.FindAll(o => o.Length > 0))
    {

    }

コンパイル先:

List<string> list = new List<string>();
if (CS$<>9__CachedAnonymousMethodDelegate1 == null)
{
    CS$<>9__CachedAnonymousMethodDelegate1 = new Predicate<string>(MyClass.<RunSnippet>b__0);
}
using (List<string>.Enumerator enumerator = list.FindAll(CS$<>9__CachedAnonymousMethodDelegate1).GetEnumerator())
{
    while (enumerator.MoveNext())
    {
        string current = enumerator.Current;
    }
}

public List<T> FindAll(Predicate<T> match)
{
    if (match == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    List<T> list = new List<T>();
    for (int i = 0; i < this._size; i++)
    {
        if (match(this._items[i]))
        {
            list.Add(this._items[i]);
        }
    }
    return list;
}

private static bool <RunSnippet>b__0(string o)
{
    return (o.Length > 0);
}
于 2009-11-12T17:24:41.540 に答える
0

パフォーマンスに問題がある場合、これはおそらくボトルネックではありませんが、並列ライブラリまたはPLINQの使用を検討しましたか?下記参照:

Parallel.ForEach(Collection, obj =>
{
    if (obj.Mandatory)
    {
        DoWork();
    }
});

http://msdn.microsoft.com/en-us/library/dd460688(v=vs.110).aspx

また、おそらくわずかに無関係であるように見えますが、パフォーマンスがあなたの好奇心をかき立てているように見えますが、非常に大きなデータセットを処理している場合は、バイナリ検索が役立つ場合があります。私の場合、2つの別々のデータリストがあります。何百万ものレコードのリストを処理する必要があり、これにより、実行ごとに文字通り指数関数的な時間を節約できました。唯一の欠点は、非常に大きなコレクションにのみ役立ち、事前に並べ替える必要があることです。また、これはかなりのオーバーヘッドを提供するConcurrentDictionaryクラスを利用していることに気付くでしょうが、これはスレッドセーフであり、非同期で管理している要件とスレッド数のために必要でした。

private ConcurrentDictionary<string, string> items;
private List<string> HashedListSource { get; set; }
private List<string> HashedListTarget { get; set; }

this.HashedListTarget.Sort();
this.items.OrderBy(x => x.Value);

private void SetDifferences()
{
    for (int i = 0; i < this.HashedListSource.Count; i++)
    {
        if (this.HashedListTarget.BinarySearch(this.HashedListSource[i]) < 0)
        {
            this.Mismatch.Add(items.ElementAt(i).Key);
        }
    }
}

二分探索を使用する利点を表示する例 この画像は元々、ここにあるすばらしい記事に投稿されました:http: //letsalgorithm.blogspot.com/2012/02/intersecting-two-sorted-integer-arrays.html

お役に立てれば!

于 2014-02-09T18:27:54.483 に答える