我做了功课,发现反复保证无论在 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 ms v Square2( 为 12.07±0.51 ms )
。
我对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 上运行了它,这两种方法的结果几乎相同。我很抱歉发布了这样一个误导性的观察结果。
最佳答案
任何称职的 C# 编译器都会为您执行此类微优化。仅在必要时将变量泄漏到作用域之外。
因此,如果可能,请将 double x;
保留在循环内部。
但就个人而言,如果 items[i]
是普通旧数据数组访问,那么我会写 buffer[i] = items[i] * items[i];
。 C 和 C++ 会对此进行优化,但我认为 C# 不会(还);您的反汇编意味着它没有。
关于c# - 在 for 循环内声明的变量会影响循环的性能吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42140012/