سؤال

ورمز أدناه هو التحقق من أداء ثلاث طرق مختلفة للقيام نفس الحل.

    public static void Main(string[] args)
    {
        // for loop
        {
            Stopwatch sw = Stopwatch.StartNew();

            int accumulator = 0;
            for (int i = 1; i <= 100000000; ++i)
            {
                accumulator += i;
            }

            sw.Stop();

            Console.WriteLine("time = {0}; result = {1}", sw.ElapsedMilliseconds, accumulator);
        }

        //Enumerable.Range
        {
            Stopwatch sw = Stopwatch.StartNew();

            var ret = Enumerable.Range(1, 100000000).Aggregate(0, (accumulator, n) => accumulator + n);

            sw.Stop();
            Console.WriteLine("time = {0}; result = {1}", sw.ElapsedMilliseconds, ret);
        }

        //self-made IEnumerable<int>
        {
            Stopwatch sw = Stopwatch.StartNew();

            var ret = GetIntRange(1, 100000000).Aggregate(0, (accumulator, n) => accumulator + n);

            sw.Stop();
            Console.WriteLine("time = {0}; result = {1}", sw.ElapsedMilliseconds, ret);
        }
    }

    private static IEnumerable<int> GetIntRange(int start, int count)
    {
        int end = start + count;

        for (int i = start; i < end; ++i)
        {
            yield return i;
        }
    }
}

وكانت النتائج:

time = 306; result = 987459712
time = 1301; result = 987459712
time = 2860; result = 987459712

وليس من المستغرب أن "لحلقة" أسرع من اثنين من حلول أخرى، لأن Enumerable.Aggregate يأخذ المزيد من الدعاء الأسلوب. ومع ذلك، فإنه يدهشني حقا أن "Enumerable.Range" أسرع من "IEnumerable عصامي". وأعتقد أن Enumerable.Range لن يكون الحمل أكثر من طريقة GetIntRange بسيط.

ما هي الأسباب المحتملة لهذا؟

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

المحلول

وماذا يجب Enumerable.Range يكون أي أبطأ من الخاص GetIntRange عصامي؟ في الواقع، إذا تم تحديد Enumerable.Range ك

public static class Enumerable {
    public static IEnumerable<int> Range(int start, int count) {
        var end = start + count;
        for(var current = start; current < end; ++current) {
            yield return current;
        }
    }
}

وبعد ذلك يجب أن يكون بالضبط بأسرع بك GetIntRange عصامي. هذا هو في الواقع تنفيذ مرجعية لEnumerable.Range، في غياب أي حيل من جانب المترجم أو مبرمج.

وأنت قد تريد مقارنة GetIntRange وSystem.Linq.Enumerable.Range مع تنفيذ التالية (طبعا، يجمع في وضع الإفراج عنهم، كما يشير روب الخروج). هذا التطبيق قد يكون الأمثل قليلا فيما يتعلق بما مترجم من شأنه أن يولد من كتلة مكرر.

public static class Enumerable {
    public static IEnumerable<int> Range(int start, int count) {
        return new RangeEnumerable(start, count);
    }
    private class RangeEnumerable : IEnumerable<int> {
        private int _Start;
        private int _Count;
        public RangeEnumerable(int start, int count) {
            _Start = start;
            _Count = count;
        }
        public virtual IEnumerator<int> GetEnumerator() {
            return new RangeEnumerator(_Start, _Count);
        }
        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }
    }
    private class RangeEnumerator : IEnumerator<int> {
        private int _Current;
        private int _End;
        public RangeEnumerator(int start, int count) {
            _Current = start - 1;
            _End = start + count;
        }
        public virtual void Dispose() {
            _Current = _End;
        }
        public virtual void Reset() {
            throw new NotImplementedException();
        }
        public virtual bool MoveNext() {
            ++_Current;
            return _Current < _End;
        }
        public virtual int Current { get { return _Current; } }
        object IEnumerator.Current { get { return Current; } }
    }
}

نصائح أخرى

وتخميني هو أن كنت تعمل في مصحح. وهنا النتائج بلدي، بعد أن بنيت من سطر الأوامر مع "/ س + / التصحيح -"

time = 142; result = 987459712
time = 1590; result = 987459712
time = 1792; result = 987459712

لا يزال هناك اختلاف طفيف، ولكنها ليست وضوحا كما. مكرر كتلة تطبيقات ليست تماما فعالة كما حل مصممة خصيصا، ولكنها جيدة.

وعلى افتراض هذا هو إنشاء إصدار التشغيل، على خلاف جميع المقارنات من حيث JIT لن تعمل خارج الشقة.

هل يمكن أن ننظر إلى التجمع مع عاكس ونرى ما هي "العائد "بيان يجري توسيعها أيضا. مترجم سيتم إنشاء فئة لتغليف مكرر. ربما يكون هناك المزيد من التدبير المنزلي يجري في الشفرة التي تم إنشاؤها من تنفيذ Enumerable.Range التي من المرجح اليد مشفرة

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

public static IEnumerable<int> Range(int start, int count) {
    for(int current = 0; current < count; ++current) {
        yield return start + current;
    }
}

وهذا هو، بدلا من متغير محلي آخر، أنها تنطبق إضافة اضافية لكل محصول.

ولقد حاولت لقياس هذا، ولكن لا أستطيع التوقف عن العمليات الخارجية بما فيه الكفاية للحصول على نتائج مفهومة. كما أنني حاولت كل اختبار مرتين لتجاهل آثار مترجم JIT، ولكن حتى هذا له نتائج "مثيرة للاهتمام".

وفيما يلي عينة من نتائجي:

Run 0:
time = 4149; result = 405000000450000000
time = 25645; result = 405000000450000000
time = 39229; result = 405000000450000000
time = 29872; result = 405000000450000000

time = 4277; result = 405000000450000000
time = 26878; result = 405000000450000000
time = 26333; result = 405000000450000000
time = 26684; result = 405000000450000000

Run 1:
time = 4063; result = 405000000450000000
time = 22714; result = 405000000450000000
time = 34744; result = 405000000450000000
time = 26954; result = 405000000450000000

time = 4033; result = 405000000450000000
time = 26657; result = 405000000450000000
time = 25855; result = 405000000450000000
time = 25031; result = 405000000450000000

Run 2:
time = 4021; result = 405000000450000000
time = 21815; result = 405000000450000000
time = 34304; result = 405000000450000000
time = 32040; result = 405000000450000000

time = 3993; result = 405000000450000000
time = 24779; result = 405000000450000000
time = 29275; result = 405000000450000000
time = 32254; result = 405000000450000000

ورمز

using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;

namespace RangeTests
{
  class TestRange
  {
    public static void Main(string[] args)
    {
      for(int l = 1; l <= 2; ++l)
      {
        const int N = 900000000;
        System.GC.Collect(2);
        // for loop
        {
            Stopwatch sw = Stopwatch.StartNew();

            long accumulator = 0;
            for (int i = 1; i <= N; ++i)
            {
                accumulator += i;
            }

            sw.Stop();

            Console.WriteLine("time = {0}; result = {1}", sw.ElapsedMilliseconds, accumulator);
        }
        System.GC.Collect(2);

        //Enumerable.Range
        {
            Stopwatch sw = Stopwatch.StartNew();

            var ret = Enumerable.Range(1, N).Aggregate(0, (long accumulator,int n) => accumulator + n);

            sw.Stop();
            Console.WriteLine("time = {0}; result = {1}", sw.ElapsedMilliseconds, ret);
        }
        System.GC.Collect(2);

        //self-made IEnumerable<int>
        {
            Stopwatch sw = Stopwatch.StartNew();

            var ret = GetIntRange(1, N).Aggregate(0, (long accumulator,int n) => accumulator + n);

            sw.Stop();
            Console.WriteLine("time = {0}; result = {1}", sw.ElapsedMilliseconds, ret);
        }
        System.GC.Collect(2);

        //self-made adjusted IEnumerable<int>
        {
            Stopwatch sw = Stopwatch.StartNew();

            var ret = GetRange(1, N).Aggregate(0, (long accumulator,int n) => accumulator + n);

            sw.Stop();
            Console.WriteLine("time = {0}; result = {1}", sw.ElapsedMilliseconds, ret);
        }
        System.GC.Collect(2);
        Console.WriteLine();
    } }

    private static IEnumerable<int> GetIntRange(int start, int count)
    {
        int end = start + count;

        for (int i = start; i < end; ++i)
        {
            yield return i;
        }
    }

    private static IEnumerable<int> GetRange(int start, int count)
    {
        for (int i = 0; i < count; ++i)
        {
            yield return start + i;
        }
    }
} }

وجمعت مع

csc.exe -optimize+ -debug- RangeTests.cs
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top