考虑以下代码:
using System;
using System.Diagnostics;
namespace Demo
{
class Program
{
static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
for (int trial = 0; trial < 4; ++trial)
{
sw.Restart();
loop1();
Console.WriteLine("loop1() took " + sw.Elapsed);
sw.Restart();
loop2();
Console.WriteLine("loop2() took " + sw.Elapsed);
sw.Restart();
loop3();
Console.WriteLine("loop3() took " + sw.Elapsed);
// Console.WriteLine(); // <-- Uncomment this and the timings change a LOT!
}
}
static void loop1()
{
bool done = false;
for (int i = 0; i < 100000 && !done; ++i)
{
for (int j = 0; j < 100000 && !done; ++j)
{
for (int k = 0; k < 2; ++k)
{
if (i == 9900)
{
done = true;
break;
}
}
}
}
}
static void loop2()
{
for (int i = 0; i < 100000; ++i)
{
for (int j = 0; j < 100000; ++j)
{
for (int k = 0; k < 2; ++k)
{
if (i == 9900)
{
goto exit;
}
}
}
}
exit: return;
}
static void loop3()
{
for (int i = 0; i < 100000; ++i)
{
for (int j = 0; j < 100000; ++j)
{
for (int k = 0; k < 2; ++k)
{
if (i == 9900)
{
k = 2;
j = 100000;
i = 100000;
}
}
}
}
}
}
}
当我使用 Visual Studio 2010 在 Windows 7 x64 上编译并运行此版本的 RELEASE x86 版本时,我得到以下时序(在 Intel Core i7 上运行)
loop1() took 00:00:01.7935267
loop2() took 00:00:01.4747297
loop3() took 00:00:05.6677592
loop1() took 00:00:01.7654008
loop2() took 00:00:01.4818888
loop3() took 00:00:05.7656440
loop1() took 00:00:01.7990239
loop2() took 00:00:01.5019258
loop3() took 00:00:05.7979425
loop1() took 00:00:01.8356245
loop2() took 00:00:01.5688070
loop3() took 00:00:05.7238753
这本身就很奇怪 - 为什么 loop3() 会比其他循环慢得多?无论如何,然后我取消注释指示的行(Console.WriteLine()),我的时间变成:
loop1() took 00:00:01.8229538
loop2() took 00:00:07.8174210
loop3() took 00:00:01.4879274
loop1() took 00:00:01.7691919
loop2() took 00:00:07.4781999
loop3() took 00:00:01.4810248
loop1() took 00:00:01.7749845
loop2() took 00:00:07.5304738
loop3() took 00:00:01.4634904
loop1() took 00:00:01.7521282
loop2() took 00:00:07.6325186
loop3() took 00:00:01.4663219
现在 loop2() 慢得多,而 loop3() 快得多。我觉得这个最好奇...
所以我有两个问题:
- 其他人可以复制这个吗,和
- 如果是这样,任何人都可以解释一下吗?
[编辑] 我应该补充一点,我可以用秒表验证这些时间,并且我正在从命令行运行测试程序(因此我们可以排除 Visual Studio 干扰它)。
附录:
我对程序进行了如下修改,以排除 JITTER 正在优化循环的可能性:
using System;
using System.Diagnostics;
using System.Text;
namespace Demo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(test());
}
static string test()
{
Stopwatch sw = new Stopwatch();
int total = 0;
StringBuilder builder = new StringBuilder();
for (int trial = 0; trial < 2; ++trial)
{
sw.Restart();
total += loop1();
builder.AppendLine("loop1() took " + sw.Elapsed);
sw.Restart();
total += loop2();
builder.AppendLine("loop2() took " + sw.Elapsed);
sw.Restart();
total += loop3();
builder.AppendLine("loop3() took " + sw.Elapsed);
//builder.AppendLine(); // Uncommenting this line makes a big difference!
}
builder.AppendLine(total.ToString());
return builder.ToString();
}
static int loop1()
{
bool done = false;
int total = 0;
for (int i = 0; i < 100000 && !done; ++i)
{
for (int j = 0; j < 100000 && !done; ++j)
{
for (int k = 0; k < 2; ++k)
{
if (i == 9900)
{
done = true;
break;
}
++total;
}
}
}
return total;
}
static int loop2()
{
int total = 0;
for (int i = 0; i < 100000; ++i)
{
for (int j = 0; j < 100000; ++j)
{
for (int k = 0; k < 2; ++k)
{
if (i == 9900)
{
goto exit;
}
++total;
}
}
}
exit: return total;
}
static int loop3()
{
int total = 0;
for (int i = 0; i < 100000; ++i)
{
for (int j = 0; j < 100000; ++j)
{
for (int k = 0; k < 2; ++k)
{
if (i == 9900)
{
k = 2;
j = 100000;
i = 100000;
}
else
{
++total;
}
}
}
}
return total;
}
}
}
现在我的结果如下:
builder.AppendLine() 注释掉:
loop1() took 00:00:06.6509471
loop2() took 00:00:06.7322771
loop3() took 00:00:01.5361389
loop1() took 00:00:06.5746730
loop2() took 00:00:06.7051531
loop3() took 00:00:01.5027345
-1004901888
builder.AppendLine() 未注释掉:
loop1() took 00:00:06.9444200
loop2() took 00:00:02.8960563
loop3() took 00:00:01.4759535
loop1() took 00:00:06.9036553
loop2() took 00:00:03.1514154
loop3() took 00:00:01.4764172
-1004901888
当我这样做时,请注意 loop2() 时间的差异。不计算!