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

Sam*_*Sam 0 .net c# memory variables

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

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

// 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
}
Run Code Online (Sandbox Code Playgroud)

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

// 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
}
Run Code Online (Sandbox Code Playgroud)

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

基准1:9,419.7毫秒.

基准2:12,262毫秒.

正如你可以看到访问"新的"变量在上述基准是23.18%(2842.3 ms)快,但为什么呢?

usr*_*usr 5

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

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

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

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

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

或者,尝试使用VC++,GCC或Clang.他们都应该删除整个循环.如果他们不这样做,我会非常失望的.

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

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