首页 文章

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

提问于
浏览
10

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

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

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上运行它了 results are nearly identical for the two methods. 对于发布这样一个误导性的观察,我道歉 .

2 回答

  • 1

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

    因此,如果可能,请将 double x; 保留在循环内部 .

    就个人而言,如果 items[i] 是普通的数据阵列访问,那么我会写 buffer[i] = items[i] * items[i]; . C和C会对此进行优化,但我不会't think C# does (yet); your disassembly implies that it doesn't .

  • 5

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

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

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

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

    Prevent .NET Garbage collection for short period of time

相关问题