我已经完成了我的作业,并且发现多次保证在for循环内或外声明变量不会影响性能,而且实际上编译为相同的MSIL。但是我还是进行了一些尝试,并发现将变量声明移到循环内部确实会导致明显且稳定的性能提升。
我编写了一个小型的控制台测试类来衡量这种效果。我初始化了一个静态的double[]
数组 items,并且有两个方法对其进行循环操作,将结果写入到另一个静态的double[]
数组 buffer 中。最初,我通过观察差异发现这种变化的方法是复数的模运算。对于长度为1000000的items数组进行100次循环,我得到的运行时间在将变量(6个double
变量)放在循环内部的方法中始终更短:例如,在老式Intel Core 2 Duo @2.66 GHz配置下,32.83±0.64 ms v 43.24±0.45 ms。我尝试按不同的顺序执行它们,但没有影响结果。
然后我意识到计算复数的模远非最简单的工作示例,并测试了两种简单得多的方法:
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上运行了它,两种方法的结果几乎相同。非常抱歉我发表了这样一个具有误导性的观察。