2010-10-12 4 views
6

Быстро ли объявлять переменные внутри цикла или вне цикла? Например:Быстрее объявлять переменные внутри цикла или вне цикла?

' 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 

Какой из них быстрее? Зачем? Я предполагаю, что последнее быстрее, потому что оно просто повторно использует один и тот же «указатель» для ссылки на новое значение за кулисами вместо того, чтобы создавать новый указатель на каждой итерации, правильно? Может кто-то уточнить?

Благодаря

Обновлено:

Компилятор достаточно умен, чтобы оптимизировать код при генерации промежуточного языка. Он перемещает объявления переменных в начало метода. Ниже, является declartions в пределах 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 
+1

Возможно, компилятор оптимизирует его? Лучший совет: запустите свою IDE, создайте Секундомер и запустите несколько тысяч итераций каждой версии и посмотрите, есть ли реальная разница. –

+0

Хорошая идея! Brb с результатами. – Moderator71

+4

Вы пытаетесь решить настоящую проблему с производительностью, или вам просто скучно и интересно с микро-оптимизациями, которые никогда не составят значимой разницы в приложении реального мира? – JohnFx

ответ

11

Согласен с ответом Кевина, определите переменные, где они имеют смысл. Беспокоитесь об оптимизации, если и когда они представляют себя, и вы знаете, что проблема с переменной является проблемой. Тем не менее, рассмотрим следующие два фрагмента кода

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 

видят разницы? Эти компиляторы, они умные.

+0

Использование секундомера было не очень полезно. У меня были разные результаты, но, глядя на ИЛ, было benificial. По сути, компилятор перемещает объявление в начало метода в IL. Я отредактирую свой пост, чтобы дать разъяснения IL. – Moderator71

2

я мог себе представить, что оптимизатор знает, что это то же самое, и поэтому они оказываются иметь такую ​​же производительность. Возможно, это не так. Вы можете либо проверить объектный код, либо измерить.

4

Ни то, ни другое. Вы по-прежнему создаете новую строку в каждой итерации цикла, поэтому они будут одинаковыми. Даже если есть одно, то, что вы принимаете, невероятно незначительно в большом объеме вещей.

Объявление области видимости переменной - это то, что изменится, и если вы не нуждаетесь в ней вне цикла, тогда вы должны поместить ее внутрь.

Смежные вопросы