• for VS. foreach 那个性能更高,为什么,怎么选择


    forforeach 的效率问题是个老问题了,从网上看到的是众说纷纭,有说for效率高的也有说foreach效率高的,还有说测试方法有问题的;鉴于此,我就自己做了个试验证明一下,然后探究一下可能的原因。

     

    先看测试结果:


    我的测试结果是for的效率要比foreach高出一截来。

     

    再看测试代码(如果大家觉得我的测试有问题,请提出来呀):

     

    forVSforeach

     

    原因分析:

    为了使一个类实例可以被foreach 需要继承 IEnumerable 接口,而此接口只有一个方法,返回一个IEnumerator的接口实例,这就说明foreach是通过操纵IEnumerator的方法来实现的。我们再看IEnumerator的两个方法和一个属性:

    属性:object Current {get;}

    方法:bool MoveNext()

             void Reset()

    我们可以推断一下foreach的实现,下面是伪代码:(下面的假设是错误的)

    IEnumerator enumerator = IEnumerableInstance. GetEnumerator();
    Object foreachObj 
    = null;
    enumerator.Reset(); 
    //有可能会在此处执行一下Reset以保证从集合的第一个元素开始foreach。

    foreachStart :
    if (enumerator.MoveNext())
    {
             foreachObj 
    = enumerator.Current;
             
    {

                       
    //this area is the foreach block
    }

    goto foreachStart;
    }

    foreachEnd:

     

     

    让我们再分析一下for(int i=0;i<array.length;i++)的可能执行过程:

    int i = 0;
    forStart:
    if(i<
    array.length)
    {
             
    {
                       
    //this area is the for block

             }

    goto forStart;
    }

    forEnd:

     

    很显然for的执行过程中少了几个方法的执行,而且要少一些额外的步骤,这是不是for效率比foreach稍高一筹的原因呢?由于以上推断纯属个人推断,所以还不敢下结论,请大家帮忙找一下理论根据。

     

    最后:

    虽然说for比foreach效率稍微高一点,但是foreach的更优雅一点,另外for除了效率之外也有一个优点就是我们可以在执行for的过程中更新,删除集合的元素值,而在foreach中这是不允许的。

     

    如果既可以使用for也可以使用foreach的时候,我们可以使用foreach使代码优雅一点,除非这段代码需要特别注意性能。

     

    请大家注意:

    以上的结论和推断都是错误的,上面的那个dos窗口图片中的执行结果,是在调试模式下面的执行结果,调试模式下没有性能优化,和实际结果相差很远,实际的编译成release版本的执行结果如下:


    大家可以看到经过优化之后,是foreach的性能更高一点。

     

    理论根据IL编译代码(来自一位老外的blog http://blogs.msdn.com/kevin_ransom/archive/2004/04/19/116072.aspx ):

        static void One()
        {
            int[] a = new int[10000];
            foreach(int i in a)
                Console.WriteLine(i);
        }

      static void Two()
        {
            int[] a = new int[10000];
            for(int j=0; j<a.Length; j++)
           {
                int i = a[j];
                Console.WriteLine(i);
            }
        }

    .method private hidebysig static void One() cil managed
    {
    // Code size 38 (0x26)
    .maxstack 2
    .locals init (int32[] V_0,
    int32 V_1,
    int32[] V_2,
    int32 V_3)

    .method private hidebysig static void Two() cil managed
    {
    // Code size 36 (0x24)
    .maxstack 2
    .locals init (int32[] V_0,
    int32 V_1,
    int32 V_2)

    IL_0000: ldc.i4 0x2710
    IL_0005: newarr [mscorlib]System.Int32
    IL_000a: stloc.0
    IL_000b: ldloc.0
    IL_000c: stloc.2
    IL_000d: ldc.i4.0
    IL_000e: stloc.3
    IL_000f: br.s IL_001f

    IL_0000: ldc.i4 0x2710
    IL_0005: newarr [mscorlib]System.Int32
    IL_000a: stloc.0
    IL_000b: ldc.i4.0
    IL_000c: stloc.1
    IL_000d: br.s IL_001d


    IL_0011: ldloc.2
    IL_0012: ldloc.3
    IL_0013: ldelem.i4
    IL_0014: stloc.1
    IL_0015: ldloc.1
    IL_0016: call void [mscorlib]System.Console::WriteLine(int32)
    IL_001b: ldloc.3
    IL_001c: ldc.i4.1
    IL_001d: add
    IL_001e: stloc.3
    IL_001f: ldloc.3
    IL_0020: ldloc.2
    IL_0021: ldlen
    IL_0022: conv.i4
    IL_0023: blt.s IL_0011

    IL_000f: ldloc.0
    IL_0010: ldloc.1
    IL_0011: ldelem.i4
    IL_0012: stloc.2
    IL_0013: ldloc.2
    IL_0014: call void [mscorlib]System.Console::WriteLine(int32)
    IL_0019: ldloc.1
    IL_001a: ldc.i4.1
    IL_001b: add
    IL_001c: stloc.1
    IL_001d: ldloc.1
    IL_001e: ldloc.0
    IL_001f: ldlen
    IL_0020: conv.i4
    IL_0021: blt.s IL_000f

    IL_0025: ret
    } // end of method MyClass::One


    IL_0023: ret
    } // end of method MyClass::Two

    上面的表格是forforeach编译出来的IL代码,蓝色部分是循环的部分,执行步骤完全相同,所以理论上应该forforeach的性能应该是一样的。

    大家可以参考下面的三篇文章:
    http://blogs.msdn.com/kevin_ransom/archive/2004/04/19/116072.aspx

    http://blogs.msdn.com/brada/archive/2004/04/29/123105.aspx

    http://www.cnblogs.com/WuCountry/archive/2007/02/27/658710.html

  • 相关阅读:
    Wannafly Camp 2020 Day 2D 卡拉巴什的字符串
    [POI2010] GIL-Guilds
    Wannafly Camp 2020 Day 1D 生成树
    [AH2017/HNOI2017] 影魔
    机器学习之决策树
    终端多窗口分屏Terminator
    python的面对对象
    安装 Google BBR 加速VPS网络
    DNSLOG在渗透测试中的玩法儿
    如何利用GitHub搜索敏感信息
  • 原文地址:https://www.cnblogs.com/yukaizhao/p/forvsforeach.html
Copyright © 2020-2023  润新知