C#迭代: foreach() ?for() ? 还是指针 ?
测试了一下迭代数组的三种方法,如下:方法一:每个数组元素只用一次:
using System;
namespace Test
{
class Program
{
static unsafe void Main(string[] args)
{
long vTickCount = Environment.TickCount;
bool[] a = new bool[100000000];
bool b;
foreach (bool i in a)
b = i;
Console.WriteLine("耗时{0}毫秒", Environment.TickCount - vTickCount);
vTickCount = Environment.TickCount;
for (int i = 0; i < 100000000; i++)
b = a[i];
Console.WriteLine("耗时{0}毫秒", Environment.TickCount - vTickCount);
vTickCount = Environment.TickCount;
fixed (bool* end = &a[100000000-1])
{
bool* i = end - 100000000;
while (i++ < end)
b = *i;
}
Console.WriteLine("耗时{0}毫秒", Environment.TickCount - vTickCount);
Console.Read();
}
}
}
结果:
1873毫秒
1332毫秒
1312毫秒
可以看出,本轮测试 foreach() 最慢,for() 和指针差不多,指针代码不够直观,且要修改项目属性支持不安全代码。
方法二:每个数组元素重复使用多次:
using System;
namespace Test
{
class Program
{
static unsafe void Main(string[] args)
{
long vTickCount = Environment.TickCount;
bool[] a = new bool[100000000];
bool b;
foreach (bool i in a)
for (int j = 0; j < 20; j++)
b = i;
Console.WriteLine("耗时{0}毫秒", Environment.TickCount - vTickCount);
vTickCount = Environment.TickCount;
for (int i = 0; i < 100000000; i++)
for (int j = 0; j < 20; j++)
b = a[i];
Console.WriteLine("耗时{0}毫秒", Environment.TickCount - vTickCount);
vTickCount = Environment.TickCount;
fixed (bool* end = &a[100000000 - 1])
{
bool* i = end - 100000000;
while (i++ < end)
for (int j = 0; j < 20; j++)
b = *i;
}
Console.WriteLine("耗时{0}毫秒", Environment.TickCount - vTickCount);
Console.Read();
}
}
}
结果:
14371毫秒
22172毫秒
13610毫秒
可以看出,本轮测试 for() 最慢,foreach() 和指针差不多,指针还是最快。
总结:
foreach()每次迭代,都牵扯到几次写内存操作,这很耽误时间(后台的),但重复使用单个数组元素时,读取快捷的优势就明显了。适合重复使用每次迭代出的变量。还有个缺点,它对于数组相当于只读的。
for()迭代写内存次数比较少,但读内存比较多,每次要读取i,然后寻址再读取,从这里我们也看出来写一次内存比读一次内存耗费时间多。适合少量使用每次迭代出的变量。个人认为10次以下比foreach()快。而且读写数组比较方便。
指针就不说了,快是快,也不至于为了这几毫秒而使代码变成天书吧。 --------------------编程问答-------------------- bu cuo --------------------编程问答-------------------- 你对调foreach()和for()语句块在程序中的顺序试试。多运行几次,结果也可能不一样。
using System;
namespace Test
{
class Program
{
static unsafe void Main()
{
bool[] a = new bool[100000000];
bool b;
int vTickCount;
vTickCount = Environment.TickCount;
for (int i = 0; i < 100000000; i++)
b = a[i];
Console.WriteLine("耗时{0}毫秒", Environment.TickCount - vTickCount);
vTickCount = Environment.TickCount;
foreach (bool i in a)
b = i;
Console.WriteLine("耗时{0}毫秒", Environment.TickCount - vTickCount);
vTickCount = Environment.TickCount;
fixed (bool* end = &a[100000000-1])
{
bool* i = end - 100000000;
while (i++ < end)
b = *i;
}
Console.WriteLine("耗时{0}毫秒", Environment.TickCount - vTickCount);
}
}
}
耗时391毫秒
耗时370毫秒
耗时401毫秒
--------------------编程问答--------------------
using System;
using System.Windows.Forms;
class Test : Form
{
TextBox textBox1;
Test()
{
textBox1 = new TextBox();
textBox1.Parent = this;
textBox1.Dock = DockStyle.Fill;
textBox1.Multiline = true;
textBox1.WordWrap = false;
Button button1 = new Button();
button1.Parent = this;
button1.Text = "&Test";
button1.Dock = DockStyle.Top;
button1.Click += new EventHandler(Button1_Click);
}
unsafe void Button1_Click(object sender, EventArgs e)
{
(sender as Button).Enabled = false;
bool[] a = new bool[100000000];
bool b;
int vTickCount;
vTickCount = Environment.TickCount;
for (int i = 0; i < 100000000; i++)
b = a[i];
textBox1.Text += " for:" + (Environment.TickCount - vTickCount).ToString();
vTickCount = Environment.TickCount;
foreach (bool i in a)
b = i;
textBox1.Text += " foreach:" + (Environment.TickCount - vTickCount).ToString();
vTickCount = Environment.TickCount;
fixed (bool* end = &a[100000000-1])
{
bool* i = end - 100000000;
while (i++ < end)
b = *i;
}
textBox1.Text += " point:" + (Environment.TickCount - vTickCount).ToString();
textBox1.Text += Environment.NewLine;
textBox1.Refresh();
(sender as Button).Enabled = true;
}
static void Main()
{
Application.Run(new Test());
}
}
/*--------------------编程问答--------------------
for:391 foreach:351 point:440
for:390 foreach:361 point:520
for:391 foreach:350 point:391
for:380 foreach:401 point:441
for:381 foreach:400 point:431
for:391 foreach:390 point:431
for:401 foreach:360 point:431
for:381 foreach:350 point:441
for:430 foreach:361 point:441
for:390 foreach:361 point:471
for:431 foreach:361 point:430
for:390 foreach:401 point:380
for:391 foreach:360 point:471
for:380 foreach:361 point:531
for:421 foreach:360 point:431
for:390 foreach:401 point:431
for:391 foreach:371 point:430
for:381 foreach:360 point:441
*/
for:390 foreach:361 point:471
for:431 foreach:361 point:430
for:390 foreach:401 point:380
注意这几行,结果截然不同,有很大的随机性。 --------------------编程问答-------------------- 我对调了,方法一:每个数组元素只用一次。连续测试多次,还是for比foreach快,我机子是图拉丁+512兆SD133内存,也许内存读写速度各个机器不一样吧,你是DDR内存吗? --------------------编程问答-------------------- 我的机器是 DELL Inspiron 510m 笔记本电脑。
你可以试试我写的 Windows Form 版的程序,多按几次[Test]按钮看看运行结果。
--------------------编程问答-------------------- 我的测试结果:
每元素访问一次:for总是最快,指针稍慢一点点,foreach比for效率低50%
每元素访问20次:foreach最快,指针比foreach效率低15%,for比foreach效率低90%
很奇怪的结果 --------------------编程问答-------------------- 这种测试方法并不准确.
你需要排除AppDomain里面其他线程的时间. 需要排除垃圾收集器的时间.
--------------------编程问答-------------------- 不同意这种测试方法并不准确的说法,你实际测试一下,结果还是比较一致的,只不过各个机器硬件不同,结果也不同。 --------------------编程问答-------------------- ...一致是一回事...准确以否是另外一回事.
你如果装有VS 2005, 可以直接新建测试项目,对你的代码进行测试.
或者CLRProfile, 你会发现这种方法与实际内部情况有很明显区别. --------------------编程问答-------------------- 另外,,,执着于for循环效率实际意义不大.
真实中,更多考虑的怎么减少循环, 避免将大数据操作放在循环中. --------------------编程问答-------------------- 标记一下
--------------------编程问答-------------------- 我是初学者,我就是想知道,一旦遇到for和foreach都能做的情况,选择哪个更好。所以发帖讨论,学习一下
你说的调试方法我没用过,你说的调试出来的结果和实际运行结果不一致吗?那我应该相信哪个,如果调试结果总是foreach快,而客户机器上实际运行时总是for快的话,调试结果还可信吗? --------------------编程问答-------------------- 不是调试...是测试.
用专门的性能测试工具.
我说过因为你的代码并没有排除GC和其他线程的影响,并不准确 --------------------编程问答-------------------- up --------------------编程问答-------------------- 楼主可以看看这个(仅供参考):
《Effective C#中文版:改善C#程序的50种方法》条款11:优先采用foreach循环语句 --------------------编程问答-------------------- 谢谢啦,又学到东西了 --------------------编程问答-------------------- 推荐用 shrinerain 的方法看看 CLR 底层的东西如何操作。高级语言总是帮你做了很多事情的,而自己不自知, --------------------编程问答-------------------- 对上述的代码用 ILasm 查看一下源码就知道具体的情况了。
foreach 处理 ArrayList 的时候用到了 try...finally...
for 中没有。
至于说到装箱拆箱的动作,两部分内容都有。
ps:机器有差别代码可能有点不同,大家要看谁的性能好,就看一下ILAsm 的代码就一目了然了。
补充:.NET技术 , C#