【问题标题】:Faster to declare variables inside a loop or outside a loop?更快地在循环内或循环外声明变量?
【发布时间】:2014-02-05 20:16:48
【问题描述】:

在循环内或循环外声明变量更快吗?例如:

' Declaration inside of the loop
For each item in items
    Dim newVariable as String = GetAString()
Next

' Declaration outside of the loop
Dim newVariable as String = String.Empty
For each item in items
    newVariable = GetAString()
Next

哪个更快?为什么?我认为后者更快,因为它只是重用相同的“指针”来在幕后引用一个新值,而不是每次迭代都创建一个新指针,对吗?有人可以详细说明吗?

谢谢

更新:

编译器足够智能,可以在生成中间语言时优化代码。它将变量声明移动到方法的顶部。下面是编译后 IL 中的声明:

 .locals init ([0] string newVariable2,
           [1] int32 i,
           [2] string newVariable,
           [3] int32 V_3,
           [4] int32 VB$CG$t_i4$S0)

对于感兴趣的人,这里是整个 IL:

.method private instance void  Form1_Load(object sender,
                                          class [mscorlib]System.EventArgs e) cil managed
{
  // Code size       55 (0x37)
  .maxstack  2
  .locals init ([0] string newVariable2,
           [1] int32 i,
           [2] string newVariable,
           [3] int32 V_3,
           [4] int32 VB$CG$t_i4$S0)
  IL_0000:  nop
  IL_0001:  ldc.i4.0
  IL_0002:  stloc.1
  IL_0003:  ldarg.0
  IL_0004:  callvirt   instance string WindowsApplication1.TestVariableDeclaration::getstring()
  IL_0009:  stloc.2
  IL_000a:  nop
  IL_000b:  ldloc.1
  IL_000c:  ldc.i4.1
  IL_000d:  add.ovf
  IL_000e:  stloc.1
  IL_000f:  ldloc.1
  IL_0010:  ldc.i4     0x989680
  IL_0015:  stloc.s    VB$CG$t_i4$S0
  IL_0017:  ldloc.s    VB$CG$t_i4$S0
  IL_0019:  ble.s      IL_0003
  IL_001b:  ldc.i4.0
  IL_001c:  stloc.3
  IL_001d:  ldarg.0
  IL_001e:  callvirt   instance string WindowsApplication1.TestVariableDeclaration::getstring()
  IL_0023:  stloc.0
  IL_0024:  nop
  IL_0025:  ldloc.3
  IL_0026:  ldc.i4.1
  IL_0027:  add.ovf
  IL_0028:  stloc.3
  IL_0029:  ldloc.3
  IL_002a:  ldc.i4     0x989680
  IL_002f:  stloc.s    VB$CG$t_i4$S0
  IL_0031:  ldloc.s    VB$CG$t_i4$S0
  IL_0033:  ble.s      IL_001d
  IL_0035:  nop
  IL_0036:  ret
} // end of method TestVariableDeclaration::Form1_Load

【问题讨论】:

  • 也许编译器会优化它?最佳建议:启动您的 IDE,实例化一个 Stopwatch,然后对每个版本运行几千次迭代,看看是否有真正的不同。
  • 好主意! Brb 与结果..

标签: .net variables loops optimization declaration


【解决方案1】:

我同意凯文的回答,在有意义的地方定义变量。如果以及何时出现优化并且您知道变量声明是问题所在,请担心优化。但是,请考虑以下两段代码

void Test1()
{
    foreach (int i in Enumerable.Range(0,10))
    {
        string s = GetString();
        Console.WriteLine(s);
    }
}

void Test2()
{
    string s;
    foreach (int i in Enumerable.Range(0,10))
    {
        s = GetString();
        Console.WriteLine(s);
    }
}

以及他们生成的 IL:

Test1:
IL_0000:  ldc.i4.0    
IL_0001:  ldc.i4.s    0A 
IL_0003:  call        System.Linq.Enumerable.Range
IL_0008:  callvirt    System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator
IL_000D:  stloc.1     
IL_000E:  br.s        IL_0024
IL_0010:  ldloc.1     
IL_0011:  callvirt    System.Collections.Generic.IEnumerator<System.Int32>.get_Current
IL_0016:  pop         
IL_0017:  ldarg.0     
IL_0018:  call        UserQuery.GetString
IL_001D:  stloc.0     
IL_001E:  ldloc.0     
IL_001F:  call        System.Console.WriteLine
IL_0024:  ldloc.1     
IL_0025:  callvirt    System.Collections.IEnumerator.MoveNext
IL_002A:  brtrue.s    IL_0010
IL_002C:  leave.s     IL_0038
IL_002E:  ldloc.1     
IL_002F:  brfalse.s   IL_0037
IL_0031:  ldloc.1     
IL_0032:  callvirt    System.IDisposable.Dispose
IL_0037:  endfinally  
IL_0038:  ret         

Test2:
IL_0000:  ldc.i4.0    
IL_0001:  ldc.i4.s    0A 
IL_0003:  call        System.Linq.Enumerable.Range
IL_0008:  callvirt    System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator
IL_000D:  stloc.1     
IL_000E:  br.s        IL_0024
IL_0010:  ldloc.1     
IL_0011:  callvirt    System.Collections.Generic.IEnumerator<System.Int32>.get_Current
IL_0016:  pop         
IL_0017:  ldarg.0     
IL_0018:  call        UserQuery.GetString
IL_001D:  stloc.0     
IL_001E:  ldloc.0     
IL_001F:  call        System.Console.WriteLine
IL_0024:  ldloc.1     
IL_0025:  callvirt    System.Collections.IEnumerator.MoveNext
IL_002A:  brtrue.s    IL_0010
IL_002C:  leave.s     IL_0038
IL_002E:  ldloc.1     
IL_002F:  brfalse.s   IL_0037
IL_0031:  ldloc.1     
IL_0032:  callvirt    System.IDisposable.Dispose
IL_0037:  endfinally  
IL_0038:  ret   

看到有什么不同吗?那些编译器,他们很聪明。

【讨论】:

  • 使用秒表并不是很有用。我得到了不同的结果,但查看 IL 是有益的。本质上,编译器将声明移动到 IL 中方法的顶部。我将编辑我的帖子以澄清 IL。
【解决方案2】:

两者都没有。您仍在循环的每次迭代中创建一个新字符串,因此它们将是相同的。即使有一个,你正在做的事情在大范围内也是难以置信的可以忽略不计。

变量的作用域声明是要改变的,如果你在循环之外不需要它,那么你应该把它放在里面。

【讨论】:

    【解决方案3】:

    我可以想象优化器知道这些是相同的,因此它们具有相同的性能。不过可能不会。您可以检查目标代码或测量。

    【讨论】:

      猜你喜欢
      • 2012-02-06
      • 2010-12-25
      • 1970-01-01
      • 2014-06-15
      • 1970-01-01
      • 1970-01-01
      • 2016-12-05
      • 1970-01-01
      相关资源
      最近更新 更多