关于C#中for与foreach的性能比较测试

 关于for与foreach的争论,一直都存在。不过有些人只是拿array来测试,有些人只是拿arraylist测试,有些拿valuetype测试,有些拿引用类型测试,测试结果大相径庭,让我不知道相信哪个。于是今天自己测试了一下。

我没有做很全面的测试,只选择了其中四种,而且四种都是针对引用类型来测试的,而没有针对值类型测试。四种测试方法如下:

1、使用Array,也就是object[],比较for与foreach循环的耗时。
2、使用Array,但存放自定义类型,比较for与foreach循环的耗时。
3、使用ArrayList,存放简单的object类型,比较for与foreach循环的耗时。
4、使用ArrayList,存放自定义类型,比较for与foreach循环的耗时。

注1:我没有使用值类型来测试。foreach实际上是转换为了对枚举器的调用,而枚举器返回类型是object类型,所以值类型的数组在每次foreach访问的时候都会有一次装箱和一次拆箱的操作,这个会很大程度的影响遍历的性能。因此对值类型数组的遍历for的性能更好是有可能的。

注2:我使用的自定义类型的定义如下:

public   class  EntityBase : DictionaryBase, System.Runtime.Serialization.ISerializable, System.Runtime.Serialization.IDeserializationCallback

注3:我没有测试自定义的强类型集合或者其他实现了IList接口的类型比如DataTable,因为我觉得具体情况要取决于枚举器的实现,而实际上我们的强类型集合在继承了IList或者CollectionBase之后一般不会实现自定义的枚举器,即使实现了也是在.net的枚举器基础上做了表面上的修改而不会修改其核心算法,因此结果应该与ArrayList的测试结果一样。

注4:Dll以及Console都是在Debug模式测试的。

具体测试代码如下:

Dll中的测试方法:

     public   class  Test
    
{
        
public void ArrayTest()
        
{
            Console.WriteLine(
"ArrayTest");

            
// 初始化工作
            object[] arr = new object[100000];
            
for(int index = 0; index < 100000; index ++)
            
{
                arr[index] 
= new object();
            }

            
string str;

            
// 开始两种不同的循环
            DateTime start = DateTime.Now;
            
for(int index = 0; index < arr.Length; index ++)
            
{
                str 
= arr[index].ToString();
            }

            DateTime end 
= DateTime.Now;
            
foreach(object obj in arr)
            
{
                str 
= obj.ToString();
            }

            DateTime end2 
= DateTime.Now;

            TimeSpan span 
= end - start;
            Console.WriteLine(
"for: " + span.TotalMilliseconds.ToString());
            span 
= end2 - end;
            Console.WriteLine(
"foreach: " + span.TotalMilliseconds);
        }


        
public void MyObjectArrayTest()
        
{
            Console.WriteLine(
"MyObjectArrayTest");

            
// 初始化工作
            object[] arr = new object[100000];
            
for(int index = 0; index < 100000; index ++)
            
{
                arr[index] 
= new EntityBase();
            }

            
string str;

            
// 开始两种不同的循环
            DateTime start = DateTime.Now;
            
for(int index = 0; index < arr.Length; index ++)
            
{
                str 
= ((EntityBase)arr[index]).WriteXml(Formatting.Indented);
            }

            DateTime end 
= DateTime.Now;
            
// c# 有两种类型转换,foreach 语句中会自动执行 as 类型转换,为了尽量避免这种不一样的地方、
            
// 与 for 语句中执行同样的类型转换,没有使用 foreach(EntityBase obj in arr) 。
            foreach(object obj in arr)
            
{
                str 
= ((EntityBase)obj).WriteXml(Formatting.Indented);
            }

            DateTime end2 
= DateTime.Now;

            TimeSpan span 
= end - start;
            Console.WriteLine(
"for: " + span.TotalMilliseconds.ToString());
            span 
= end2 - end;
            Console.WriteLine(
"foreach: " + span.TotalMilliseconds);
        }


        
public void SimpleObjectListTest()
        
{
            Console.WriteLine(
"SimpleObjectListTest");

            
// 初始化工作
            ArrayList arr = new ArrayList();
            
for(int index = 0; index < 100000; index ++)
            
{
                arr.Add(
new object());
            }

            
string str;

            
// 开始两种不同的循环
            DateTime start = DateTime.Now;
            
for(int index = 0; index < arr.Count; index ++)
            
{
                str 
= arr[index].ToString();
            }

            DateTime end 
= DateTime.Now;
            
foreach(object obj in arr)
            
{
                str 
= obj.ToString();
            }

            DateTime end2 
= DateTime.Now;

            TimeSpan span 
= end - start;
            Console.WriteLine(
"for: " + span.TotalMilliseconds.ToString());
            span 
= end2 - end;
            Console.WriteLine(
"foreach: " + span.TotalMilliseconds);
        }


        
public void MyObjectListTest()
        
{
            Console.WriteLine(
"MyObjectListTest");

            
// 初始化工作
            ArrayList arr = new ArrayList();
            
for(int index = 0; index < 100000; index ++)
            
{
                arr.Add(
new EntityBase());
            }

            
string str;

            
// 开始两种不同的循环
            DateTime start = DateTime.Now;
            
for(int index = 0; index < arr.Count; index ++)
            
{
                str 
= ((EntityBase)arr[index]).WriteXml(Formatting.Indented);
            }

            DateTime end 
= DateTime.Now;
            
// c# 有两种类型转换,foreach 语句中会自动执行 as 类型转换,为了尽量避免这种不一样的地方、
            
// 与 for 语句中执行同样的类型转换,没有使用 foreach(EntityBase obj in arr) 。
            foreach(object obj in arr)
            
{
                str 
= ((EntityBase)obj).WriteXml(Formatting.Indented);
            }

            DateTime end2 
= DateTime.Now;

            TimeSpan span 
= end - start;
            Console.WriteLine(
"for: " + span.TotalMilliseconds.ToString());
            span 
= end2 - end;
            Console.WriteLine(
"foreach: " + span.TotalMilliseconds);
        }

    }

 

Console控制台的调用:

 

     class  Class1
    
{
        
Main

        
public void DoMain(string cmd, string[] args)
        
{
            TestDll.Test test 
= new TestDll.Test();
            test.ArrayTest();
            test.MyObjectArrayTest();
            test.SimpleObjectListTest();
            test.MyObjectListTest();
        }

}

 

为了去掉其他如.net机制等造成的影响,编译完成后,我通过cmd.exe先执行了几遍调用,然后开始真正的测试。

 最终输出如下:

please input command:
test
ArrayTest
for:            15.625
foreach:        15.625
MyObjectArrayTest
for:            2250
foreach:        812.5
SimpleObjectListTest
for:            15.625
foreach:        31.25
MyObjectListTest
for:            2312.5
foreach:        859.375
please input command:
exit
will exit.

// 注:上面的调用是为了消除.net机制等引起的第一次执行可能引起的额外影响。下面真正开始测试。

E:/My Projects/ConsoleTester/bin/Debug>ConsoleTester.exe
please input command:
test
ArrayTest
for:            15.625
foreach:        15.625
MyObjectArrayTest
for:            2046.875
foreach:        812.5
SimpleObjectListTest
for:            31.25
foreach:        15.625
MyObjectListTest
for:            2187.5
foreach:        890.625
please input command:
test
ArrayTest
for:            31.25
foreach:        15.625
MyObjectArrayTest
for:            2187.5
foreach:        843.75
SimpleObjectListTest
for:            31.25
foreach:        15.625
MyObjectListTest
for:            2203.125
foreach:        859.375
please input command:
test
ArrayTest
for:            15.625
foreach:        15.625
MyObjectArrayTest
for:            2312.5
foreach:        812.5
SimpleObjectListTest
for:            31.25
foreach:        15.625
MyObjectListTest
for:            2375
foreach:        843.75
please input command:
test
ArrayTest
for:            15.625
foreach:        15.625
MyObjectArrayTest
for:            2359.375
foreach:        828.125
SimpleObjectListTest
for:            15.625
foreach:        15.625
MyObjectListTest
for:            2343.75
foreach:        843.75
please input command:
test
ArrayTest
for:            15.625
foreach:        31.25
MyObjectArrayTest
for:            2218.75
foreach:        812.5
SimpleObjectListTest
for:            15.625
foreach:        46.875
MyObjectListTest
for:            2359.375
foreach:        859.375
please input command:
test
ArrayTest
for:            15.625
foreach:        31.25
MyObjectArrayTest
for:            2171.875
foreach:        843.75
SimpleObjectListTest
for:            31.25
foreach:        15.625
MyObjectListTest
for:            2375
foreach:        859.375
please input command:
test
ArrayTest
for:            15.625
foreach:        31.25
MyObjectArrayTest
for:            2203.125
foreach:        812.5
SimpleObjectListTest
for:            31.25
foreach:        15.625
MyObjectListTest
for:            2343.75
foreach:        875
please input command:
test
ArrayTest
for:            15.625
foreach:        15.625
MyObjectArrayTest
for:            2250
foreach:        812.5
SimpleObjectListTest
for:            15.625
foreach:        31.25
MyObjectListTest
for:            2312.5
foreach:        859.375
please input command:
test
ArrayTest
for:            15.625
foreach:        15.625
MyObjectArrayTest
for:            2265.625
foreach:        828.125
SimpleObjectListTest
for:            15.625
foreach:        31.25
MyObjectListTest
for:            2343.75
foreach:        859.375
please input command:
test
ArrayTest
for:            15.625
foreach:        15.625
MyObjectArrayTest
for:            2250
foreach:        843.75
SimpleObjectListTest
for:            15.625
foreach:        31.25
MyObjectListTest
for:            2406.25
foreach:        843.75
please input command:

从上面的结果来看,测试结果有些动荡,不过我们通过大多数情况,就可以得到一些一般性结论。具体结论我也就不说了。虽然测试的覆盖面并不是很全,不过个人觉得至少代表了我们实际应用中的大部分情况。

测试完毕。

顺便贴下Dll反编译的结果:(有人说反编译发现foreach都变成了for,事实好像不是这样的)

 

public   void  ArrayTest()
{
      
string text1;
      Console.WriteLine(
"ArrayTest");
      
object[] objArray1 = new object[0x186a0];
      
for (int num1 = 0; num1 < 0x186a0; num1++)
      
{
            objArray1[num1] 
= new object();
      }

      DateTime time1 
= DateTime.Now;
      
for (int num2 = 0; num2 < objArray1.Length; num2++)
      
{
            text1 
= objArray1[num2].ToString();
      }

      DateTime time2 
= DateTime.Now;
      
foreach (object obj1 in objArray1)
      
{
            text1 
= obj1.ToString();
      }

      DateTime time3 
= DateTime.Now;
      TimeSpan span1 
= (TimeSpan) (time2 - time1);
      Console.WriteLine(
"for: " + span1.TotalMilliseconds.ToString());
      span1 
= (TimeSpan) (time3 - time2);
      Console.WriteLine(
"foreach: " + span1.TotalMilliseconds);
}



public   void  MyObjectArrayTest()
{
      
string text1;
      Console.WriteLine(
"MyObjectArrayTest");
      
object[] objArray1 = new object[0x186a0];
      
for (int num1 = 0; num1 < 0x186a0; num1++)
      
{
            objArray1[num1] 
= new EntityBase();
      }

      DateTime time1 
= DateTime.Now;
      
for (int num2 = 0; num2 < objArray1.Length; num2++)
      
{
            text1 
= ((EntityBase) objArray1[num2]).WriteXml(Formatting.Indented);
      }

      DateTime time2 
= DateTime.Now;
      
foreach (object obj1 in objArray1)
      
{
            text1 
= ((EntityBase) obj1).WriteXml(Formatting.Indented);
      }

      DateTime time3 
= DateTime.Now;
      TimeSpan span1 
= (TimeSpan) (time2 - time1);
      Console.WriteLine(
"for: " + span1.TotalMilliseconds.ToString());
      span1 
= (TimeSpan) (time3 - time2);
      Console.WriteLine(
"foreach: " + span1.TotalMilliseconds);
}


 
public   void  MyObjectListTest()
{
      
string text1;
      Console.WriteLine(
"MyObjectListTest");
      ArrayList list1 
= new ArrayList();
      
for (int num1 = 0; num1 < 0x186a0; num1++)
      
{
            list1.Add(
new EntityBase());
      }

      DateTime time1 
= DateTime.Now;
      
for (int num2 = 0; num2 < list1.Count; num2++)
      
{
            text1 
= ((EntityBase) list1[num2]).WriteXml(Formatting.Indented);
      }

      DateTime time2 
= DateTime.Now;
      
foreach (object obj1 in list1)
      
{
            text1 
= ((EntityBase) obj1).WriteXml(Formatting.Indented);
      }

      DateTime time3 
= DateTime.Now;
      TimeSpan span1 
= (TimeSpan) (time2 - time1);
      Console.WriteLine(
"for: " + span1.TotalMilliseconds.ToString());
      span1 
= (TimeSpan) (time3 - time2);
      Console.WriteLine(
"foreach: " + span1.TotalMilliseconds);
}


public   void  SimpleObjectListTest()
{
      
string text1;
      Console.WriteLine(
"SimpleObjectListTest");
      ArrayList list1 
= new ArrayList();
      
for (int num1 = 0; num1 < 0x186a0; num1++)
      
{
            list1.Add(
new object());
      }

      DateTime time1 
= DateTime.Now;
      
for (int num2 = 0; num2 < list1.Count; num2++)
      
{
            text1 
= list1[num2].ToString();
      }

      DateTime time2 
= DateTime.Now;
      
foreach (object obj1 in list1)
      
{
            text1 
= obj1.ToString();
      }

      DateTime time3 
= DateTime.Now;
      TimeSpan span1 
= (TimeSpan) (time2 - time1);
      Console.WriteLine(
"for: " + span1.TotalMilliseconds.ToString());
      span1 
= (TimeSpan) (time3 - time2);
      Console.WriteLine(
"foreach: " + span1.TotalMilliseconds);
}


 
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值