当前位置:  开发笔记 > 编程语言 > 正文

为什么Enumerable.Range比直接yield循环更快?

如何解决《为什么Enumerable.Range比直接yield循环更快?》经验,为你挑选了2个好方法。

下面的代码是检查执行相同解决方案的三种不同方式的性能.

    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
        {
            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 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需要更多的方法调用,所以"for循环"比其他两个解决方案更快也就不足为奇了.然而,"Enumerable.Range"比"自制IEnumerable"更快,这让我感到惊讶.我认为Enumerable.Range比简单的GetIntRange方法有更多的开销.

这有什么可能的原因?



1> yfeldblum..:

为什么Enumerable.Range要比你的自制慢GetIntRange?实际上,如果Enumerable.Range被定义为

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

然后它应该和你自制的一样快GetIntRange.这实际上是参考实现Enumerable.Range,没有编译器或程序员的任何技巧.

您可能希望将您GetIntRangeSystem.Linq.Enumerable.Range以下实现进行比较(当然,正如Rob指出的那样,在发布模式下进行编译).关于编译器将从迭代器块生成的内容,可以稍微优化该实现.

public static class Enumerable {
    public static IEnumerable Range(int start, int count) {
        return new RangeEnumerable(start, count);
    }
    private class RangeEnumerable : IEnumerable {
        private int _Start;
        private int _Count;
        public RangeEnumerable(int start, int count) {
            _Start = start;
            _Count = count;
        }
        public virtual IEnumerator GetEnumerator() {
            return new RangeEnumerator(_Start, _Count);
        }
        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }
    }
    private class RangeEnumerator : IEnumerator {
        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; } }
    }
}



2> Jon Skeet..:

我的猜测是你在调试器中运行.以下是我的结果,从命令行使用"/ o +/debug-"构建

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

仍有一点点差异,但并不是那么明显.迭代器块实现不如定制解决方案那么高效,但它们非常好.


这里有两件事:*在调试模式下构建*,在调试器中运行*而不是在没有附加调试器的情况下执行.后者使更多的差异.
推荐阅读
刘美娥94662
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有