Why is Enumerable.Range faster than a direct yield loop?

后端 未结 4 1750
离开以前
离开以前 2020-12-09 12:07

The code below is checking performance of three different ways to do same solution.

    public static void Main(string[] args)
    {
        // for loop
             


        
4条回答
  •  无人及你
    2020-12-09 12:38

    A slight difference in the Reflector output (as well as the argument check and extra level of internalisation definitely not relevant here). The essential code is more like:

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

    That is, instead of another local variable, they apply an extra addition for every yield.

    I have tried to benchmark this, but I can't stop enough external processes to get understandable results. I also tried each test twice to ignore the effects of the JIT compiler, but even that has 'interesting' results.

    Here's a sample of my results:

    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
    

    and the code

    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
            {
                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
            {
                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 GetIntRange(int start, int count)
        {
            int end = start + count;
    
            for (int i = start; i < end; ++i)
            {
                yield return i;
            }
        }
    
        private static IEnumerable GetRange(int start, int count)
        {
            for (int i = 0; i < count; ++i)
            {
                yield return start + i;
            }
        }
    } }
    

    compiled with

    csc.exe -optimize+ -debug- RangeTests.cs
    

提交回复
热议问题