在for循环中声明的变量是否会影响循环的性能?

时间:2017-02-09 15:00:27

标签: c# for-loop variable-declaration

我已经完成了我的作业,并且一再保证,无论是在for循环内部还是外部声明变量,它在性能上都没有区别,它实际上编译为完全相同的MSIL。但是我一直在摆弄它,并发现在循环中移动变量声明确实会导致相当大的一致性能增益。

我已经编写了一个小型控制台测试类来测量这种效果。我初始化了一个静态double[]数组项,,两个方法对它执行循环操作,将结果写入静态double[]数组缓冲区。最初,我的方法是那些我注意到差异的方法,即复数的大小计算。为长度为1000000的 items 数组运行这些100次,我得到的变量(6 double变量)在循环中的运行时间一直较低:例如,32 ,对于采用英特尔酷睿2双核2.66 GHz的老式配置,83±0,64 ms v 43,44±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 ms对Square2()的12.07±0.51 ms。

我不熟悉ILDASM,但我已经拆解了这两种方法,唯一的区别似乎是局部变量的初始化:

      .locals init ([0] float64 x,
       [1] int32 i,
       [2] bool CS$4$0000)
<{1>} v

中的

Square1()
.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上运行它,结果几乎完全相同。我对发布这样一个误导性的观察表示歉意。

2 个答案:

答案 0 :(得分:5)

任何有价值的C#编译器都会为您执行此类微优化。如果必要,只在范围外泄漏变量。

如果可能,请将double x;置于循环内部。

就个人而言,如果items[i]是普通的数据阵列访问,那么我会写buffer[i] = items[i] * items[i];。 C和C ++会对此进行优化,但我不认为C#会(但);你的反汇编暗示它没有。

答案 1 :(得分:1)

分析垃圾收集器对这两种变体的作用会很有趣。

我可以想象,在第一种情况下,循环运行时不会收集变量x,因为它是在外部范围内声明的。

在第二种情况下,x上的所有句柄将在每次迭代时删除。

也许您使用新的C#4.6 GC.TryStartNoGCRegionGC.EndNoGCRegion再次运行测试,看看性能影响是否来自GC。

Prevent .NET Garbage collection for short period of time