سؤال

سؤال سريع:

أي واحد أسرع؟

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

أو

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

وإذا كنت تعرف اقتراحا أسرع، سأكون مسرورا أن أعرف.

شكرا لك

هل كانت مفيدة؟

المحلول

يقوم رمز الاختبار التالي بطباعة علامات النظام (1 علامة = 100 Nanoseconds) للتكرار من خلال 10 ملايين كائنات. البادئة أبطأ وحلقة أسرع كما هو متوقع.

ولكن يتم قياس النفقات العامة للتكرار nanoseconds. لكل عنصر حتى في أسوأ الحالات. إذا كنت تفعل أي شيء مهم في الحلقة (مثل شيء ما يأخذ microsecond لكل عنصر)، فإن الفرق السرعة في التكرار هو ضئيلة تماما.

لذلك بالنسبة لحب تورينج لا يمنع تمنع إرشادات الترميز الخاصة بك الآن. لا يجعل أي فرق عملي، وستهل تصريحات 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


      }

نصائح أخرى

إذا كان لديك Collection هو List<T> ومن بعد FindAll يتم تنفيذها عن طريق إنشاء جديد List<T> ونسخ جميع العناصر التي تتطابق مع المسند. من الواضح أن هذا أبطأ من مجرد تعداد المجموعة واتخاذ قرار لكل عنصر إذا كان المسند يحمل.

إذا كنت تستخدم .NET 3.5، فيمكنك استخدام LinQ والتي لن تخلق نسخة وتشبه مثالك الأول:

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

لاحظ أن هذا ليس بالضرورة أسرع حلا. من السهل رؤية هذه الطريقة التي تخصص الذاكرة و يعد مجموعة أبطأ من الطريقة التي فقط يعد مجموعة. إذا كان الأداء أمرا بالغ الأهمية: قياسه.

الأول سيكون أسرع إلى حد ما.

في الحالة الثانية، أنت تستخدم List<T>.FindAll لإنشاء قائمة مؤقتة تتطابق مع معاييرك. هذا نسخ القائمة، ثم يكرر فوقه.

ومع ذلك، يمكنك تحقيق نفس الشيء، بنفس السرعة مثل خيارك الأول، من خلال القيام:

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

هذا بسبب inferiable.where. يستخدم البث للعودة IEnumerable<T>, ، والتي يتم إنشاؤها كما كنت تكرر. لا يتم إصدار نسخة.

الأسرع التي يمكن أن تحصل عليها من أي وقت مضى دون توزيات التعداد في مواضيع متعددة تأخذ حسابات عدد المعالجات، إلخ:

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

أود أن أوصي بك على الرغم من استخدام LinQ دائما بدلا من الكتابة for أو foreach الحلقات لأنه في المستقبل سيصبح ذكي للغاية بحيث يكون قادرا على توزيع العمل على المعالجات وأخذ في الاعتبار أشياء محددة للأجهزة (انظر بلينك) وسوف تكون أسرع في النهاية مما لو كتبت الحلقات الخاصة بك: برمجة.

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);
}

إذا كان الأداء قيد السؤال، فليس هذا ليس عنق الزجاجة، ومع ذلك، هل فكرت في استخدام المكتبة الموازية أو بلينك؟ انظر أدناه:

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

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

أيضا، على الرغم من أنه ربما لا يرتبط به قليلا، إلا أنه على الرغم من أن الأداء يخطئ فضولي، إذا كنت تتعامل مع مجموعات كبيرة جدا من البيانات، فقد يكون البحث الثنائي مفيدا. في حالتي لدي اثنين من القوائم المنفصلة للبيانات. يجب أن أتعامل مع قوائم الملايين من السجلات وحفظني حرفيا كمية أسية من الوقت لكل عملية تنفيذ. الجانب السلبي الوحيد هو أنه مفيد فقط للمجموعات الكبيرة للغاية ويتطلب فرزه مسبقا. ستلاحظ أيضا أن هذا يستفيد من الفئة المتزامنة المتوفرة، والتي توفر مرفقا كبيرا، لكنه مؤمن للخيط ويمتد بسبب متطلبات وعدد الخيوط التي أجرتها بشكل غير متزامن.

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);
        }
    }
}

Example displaying the benefits of using Binary Searchتم نشر هذه الصورة في الأصل في مقال عظيم موجود هنا: http://letsalgorithm.blogspot.com/02/02/intersecting-two-sorted-8inger-arrays.html.

أتمنى أن يساعدك هذا!

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top