为什么访问先前创建的变量比刚刚声明的变量需要更长的时间?

时间:2014-06-02 13:50:44

标签: c# .net variables memory

我最近运行了一个基准测试,以查看在变量声明块结束时或之后声明的变量的访问时间是否更少。

基准代码(在块结束时声明的选定变量),

// Benchmark 1    
for (long i = 0; i < 6000000000; i++)
{
    var a1 = 0;
    var b1 = 0;
    var c1 = 0;

    // 53 variables later...

    var x2 = 0;
    var y2 = 0;
    var z2 = 0;

    z2 = 1;     // Write

    var _ = z2; // Read
}

基准代码(在块开始时声明的选定变量),

// Benchmark 2    
for (long i = 0; i < 6000000000; i++)
{
    var a1 = 0;
    var b1 = 0;
    var c1 = 0;

    // 53 variables later...

    var x2 = 0;
    var y2 = 0;
    var z2 = 0;

    a1 = 1;     // Write

    var _ = a1; // Read
}

令我惊讶的是,结果(平均超过3次运行,不包括首次构建和没有优化)如下,

  

基准1:9,419.7毫秒。

     

基准2:12,262毫秒。

正如您所看到的那样,访问&#34;更新&#34;上述基准中的变量23.18%2842.3 ms)更快,但为什么?

2 个答案:

答案 0 :(得分:5)

通常,在世界上基本上任何优化编译器中都会通过优化删除未使用的本地。您只写大多数变量。这是删除其物理存储空间的简单案例。

逻辑本地与其物理存储之间的关系非常复杂。它们可能会被删除,注册或泄露。

所以不要认为var _ = a1;实际上会导致a1的读取和_的写入。它什么都不做。

JIT在许多(我相信64个)局部变量的函数中关闭了一些优化,因为一些算法在本地数量上具有二次运行时间。也许这就是那些当地人影响表现的原因。

尝试使用较少的变量,您将无法区分此功能的变体。

或者,尝试使用VC ++,GCC或Clang。他们都应该删除整个循环。如果他们没有,我会非常失望。

我不认为你在这里测量相关的东西。无论你的基准测试结果如何 - 它都可以帮助你解决现实代码问题。如果这是一个有趣的案例,我会看看反汇编,但正如我所说,我认为这是无关紧要的。无论我发现什么,这都不是一个有趣的发现。

如果您想了解编译器通常生成的代码,您应该编写一些简单的函数并查看生成的机器代码。这可能非常具有教学意义。

答案 1 :(得分:1)

尝试在汇编程序中思考/更接近硬件,它可能是这样的: 在更快的版本中,您仍然具有存储在当前寄存器中的先前访问的变量z2的地址,然后可以直接再次使用该地址而无需更改其内容(=重新计算正确的存储器地址)以进行写入和读取。

它可以是由解释器/编译器完成的自动优化。 在循环结束时,您是否尝试过其他变量而不是z2进行W / R测试? 如果您使用x2或y2甚至中间的任何其他变量会发生什么? 除z2之外的所有变量的访问时间是否相等,或者它们是否也不同?