【发布时间】:2017-06-27 15:47:49
【问题描述】:
我已经完成了我的功课,发现反复保证无论您在 for 循环内部还是外部声明变量,它都不会影响性能,并且它实际上编译为相同的 MSIL。但是我一直在摆弄它,发现在循环内移动变量声明确实会带来相当大且一致的性能提升。
我编写了一个小型控制台测试类来测量这种效果。我初始化了一个静态double[] 数组items, 和两个方法对其执行循环操作,将结果写入静态double[] 数组缓冲区。 最初,我的方法是那些我注意到差异的,即复数的大小计算。为长度为 1000000 的 items 数组运行这些 100 次,对于其中变量(6 个 double 变量)在循环内的那个,我始终得到较低的运行时间:例如,32,83 ±0,64 ms v 43,24±0,45 ms,采用 Intel Core 2 Duo @2.66 GHz 的老年配置。我尝试以不同的顺序执行它们,但这并没有影响结果。
然后我意识到计算复数的大小远非最小的工作示例,并测试了两种更简单的方法:
static void Square1()
{
double x;
for (int i = 0; i < buffer.Length; i++) {
x = items[i];
buffer[i] = x * x;
}
}
static void Square2()
{
for (int i = 0; i < buffer.Length; i++) {
double x;
x = items[i];
buffer[i] = x * x;
}
}
有了这些,结果以另一种方式出现:在循环外声明变量似乎更有利:Square1() 为 7.07±0.43 毫秒,Square2() 为 12.07±0.51 毫秒。
我对ILDASM不是很熟悉,但是我把这两种方法都反汇编过了,唯一的区别似乎是局部变量的初始化:
.locals init ([0] float64 x,
[1] int32 i,
[2] bool CS$4$0000)
在Square1()v
.locals init ([0] int32 i,
[1] float64 x,
[2] bool CS$4$0000)
在Square2()。根据它,一个是stloc.1,另一个是stloc.0,反之亦然。在更长的复杂幅度计算 MSIL 代码中,甚至代码大小也不同,我在外部声明代码中看到了 stloc.s i,而内部声明代码中有 stloc.0。
那怎么可能呢?我是否忽略了某些东西,或者它是一个真正的效果?如果是这样,它会对长循环的性能产生显着影响,所以我认为值得讨论。
非常感谢您的想法。
编辑:我忽略的一件事是在发布之前在多台计算机上对其进行测试。我现在已经在 i5 上运行它,两种方法的结果几乎相同。我很抱歉发布了这样的误导性观察。
【问题讨论】:
-
很好的调查,你肯定会获得支持。
-
@NicoRiff:确实,这是一个写得很好的问题。 (可悲的是,虽然我认为答案是微不足道的。)
-
我等不及@JonSkeet 回答这个问题了
-
我无法使用给定的代码复制此行为。生成的 IL 肯定会按照声明 locals 的顺序翻转,但我没有看到任何显着的性能差异。
-
你能展示你用来衡量性能的代码吗?代码第一次运行时是否考虑到了 JIT 编译?
标签: c# for-loop variable-declaration