在循环内或循环外声明变量更快?

发布于 2024-09-27 07:37:51 字数 2040 浏览 0 评论 0原文

在循环内声明变量更快还是在循环外声明变量更快?例如:

' 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

Is it faster to declare variables inside a loop or outside a loop? For example:

' 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

Which one is faster? Why? I assume the latter is faster because it is just reusing the same "pointer" to reference a new value behind the scenes instead of creating a new pointer each iteration, correct? Can someone elaborate?

Thanks

Updated:

The Compiler is intelligent enough to optimize the code when generating the Intermediate Language. It moves the variable declarations to the top of the method. Below, is the declartions within the IL after compilation:

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

Here's the entire IL for those interested:

.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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

笑看君怀她人 2024-10-04 07:37:51

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

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   

看到什么区别了吗?那些编译器家伙,他们很聪明。

I agree with Kevin's answer, define variables where they have meaning. Worry about optimizations if and when they present themselves and you know that a variable declaration is the issue. However, consider the following two pieces of code

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);
    }
}

And their generated 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   

See any difference? Those compiler guys, they're smart.

寒江雪… 2024-10-04 07:37:51

两者都不。您仍在循环的每次迭代中创建一个新字符串,因此它们将是相同的。即使有,你所涉及的东西在大范围内也是微不足道的。

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

Neither. You are still creating a new string in each iteration of the loop, so they will be the same. Even if there is one, what you are taking about is unbelievable negligible in the big scope of things.

The scope declaration of the variable is what will change, and if you don't need it outside of the loop, then you should place it inside.

泪是无色的血 2024-10-04 07:37:51

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

I could imagine that the optimizer knows that these are the same and therefore they turn out to have the same performance. It might not though. You could either inspect the object code or measure.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文