Использует ли один из них больше ресурсов, чем другой?

Что происходит в фоновом режиме для этих двух кодовых блоков? Будет ли считаться "лучше", чем другой?

Моя мысль заключается в том, что Example2 может быть хуже, потому что ему может потребоваться уничтожить сборщик мусора, но я не знаю достаточно о сборщике мусора, чтобы узнать, правда ли это.

Пример1:

ListItem item;
for (int i = 1; i <= 32; i++)
{
   item = new ListItem();
   //do some stuff
}

Пример 2:

for (int i = 1; i <= 32; i++)
{
   ListItem item = new ListItem();
   //do some stuff
}

Ответ 1

Я скопировал ваш код в Visual Studio, скомпилировал его, а затем посмотрел на сгенерированный IL. Это IL, созданный в примере 1:

.method private hidebysig static void  One() cil managed
{
  // Code size       30 (0x1e)
  .maxstack  2
  .locals init ([0] class WinTest.ListItem item,
           [1] int32 i,
           [2] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  stloc.1
  IL_0003:  br.s       IL_0011
  IL_0005:  nop
  IL_0006:  newobj     instance void WinTest.ListItem::.ctor()
  IL_000b:  stloc.0
  IL_000c:  nop
  IL_000d:  ldloc.1
  IL_000e:  ldc.i4.1
  IL_000f:  add
  IL_0010:  stloc.1
  IL_0011:  ldloc.1
  IL_0012:  ldc.i4.s   32
  IL_0014:  cgt
  IL_0016:  ldc.i4.0
  IL_0017:  ceq
  IL_0019:  stloc.2
  IL_001a:  ldloc.2
  IL_001b:  brtrue.s   IL_0005
  IL_001d:  ret
} // end of method Program::One

И это IL, сгенерированный из примера 2:

.method private hidebysig static void  Two() cil managed
{
  // Code size       30 (0x1e)
  .maxstack  2
  .locals init ([0] int32 i,
           [1] class WinTest.ListItem item,
           [2] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  stloc.0
  IL_0003:  br.s       IL_0011
  IL_0005:  nop
  IL_0006:  newobj     instance void WinTest.ListItem::.ctor()
  IL_000b:  stloc.1
  IL_000c:  nop
  IL_000d:  ldloc.0
  IL_000e:  ldc.i4.1
  IL_000f:  add
  IL_0010:  stloc.0
  IL_0011:  ldloc.0
  IL_0012:  ldc.i4.s   32
  IL_0014:  cgt
  IL_0016:  ldc.i4.0
  IL_0017:  ceq
  IL_0019:  stloc.2
  IL_001a:  ldloc.2
  IL_001b:  brtrue.s   IL_0005
  IL_001d:  ret
} // end of method Program::Two

Насколько я понимаю, они идентичны, за исключением того, что локальные жители объявляются (и, следовательно, обращаются) в обратном порядке. Я не ожидаю, что это повлияет на производительность.

Ответ 2

Это зависит от того, что означает "//делать некоторые вещи".

В простой программе оба примера будут скомпилированы с одним и тем же байтовым кодом MSIL.

Но если в цикле создается анонимный делегат, возможно, выполняется в другом потоке, который ссылается на переменную "item", имеет значение, был ли объявлен "элемент" внутри или вне цикла. Если, как в примере 2, "элемент" объявляется внутри цикла, тогда при запуске делегата будет видно значение "item", назначенное в итерации цикла, который создал делегат (что наиболее вероятно, что предназначено для эти случаи). Если, как в примере 1, "элемент" был объявлен вне цикла, тогда делегат увидит значение, назначенное во время его выполнения, которое может быть с более поздней итерации, чем тот, который создал делегат. Это приводит к запутыванию условий гонки.

В принципе, хотя байт-код MSIL, который компиляция С# не представляет область видимости переменной, область действия имеет смысл в С# и может влиять на то, как ведут себя различные синтаксические сахара.

Ответ 3

Удивительно, но мои тесты показывают пример 2 быстрее.

Выход. Случай 1 представляет собой единую декларацию. Случай 2 является объявлением цикла.

Case 1: 10.280418100
Case 2: 10.264818000 99.848254226%
Case 1: 10.592418600
Case 2: 10.140017800 95.729013202%
Case 1: 10.233618000
Case 2: 10.108817800 98.780487996%
Case 1: 10.155617800
Case 2: 10.046417600 98.924731098%
Case 1: 10.503818600
Case 2: 10.246319800 97.548522020%
Case 1: 10.243018400
Case 2: 10.030817600 97.928337217%
Case 1: 10.077617700
Case 2: 10.218017900 101.393188392%
Case 1: 10.303019300
Case 2: 10.526318800 102.167320991%
Case 1: 10.353619900
Case 2: 10.276219400 99.252430544%
Case 1: 10.264818100
Case 2: 10.202417900 99.392096388%

Case 1 Total: 103.007984500
Case 2 Total: 102.060182600 99.079875308%

код:

Public Sub Main()
    Dim Case1Total As Double = 0
    Dim Case2Total As Double = 0
    For i As Integer = 1 To 10
        Dim Case1 As Double = MeasureTime(AddressOf Case1Method).TotalSeconds
        Case1Total += Case1
        Console.WriteLine("Case 1: {0:N9}", Case1)
        Dim Case2 As Double = MeasureTime(AddressOf Case2Method).TotalSeconds
        Case2Total += Case2
        Console.WriteLine("Case 2: {0:N9} {1:N9}%", Case2, 100 * Case2 / Case1)
    Next i
    Console.WriteLine()
    Console.WriteLine("Case 1 Total: {0:N9}", Case1Total)
    Console.WriteLine("Case 2 Total: {0:N9} {1:N9}%", Case2Total, 100 * Case2Total / Case1Total)
    Console.ReadLine()
End Sub

Private Function MeasureTime(Method As Action) As TimeSpan
    Dim StartTime As Date = Date.Now
    Method()
    Return Date.Now - StartTime
End Function

Private Sub Case1Method()
    Dim o As Label
    For i As Integer = 0 To Limit
        o = New Label
        o.Text = "Label" & i.ToString
        o.Dispose()
    Next
End Sub

Private Sub Case2Method()
    For i As Integer = 0 To Limit
        Dim o As New Label
        o.Text = "Label" & i.ToString
        o.Dispose()
    Next
End Sub

Private Const Limit As Integer = 1024 * 1024

Ответ 4

Мое личное чувство было бы в том, что здесь будет небольшая разница (за пределами очевидной разницы в показаниях).

Любой из них выделит новую память с ссылкой на нее и оставит предыдущую память с меньшей ссылкой, готов к GC'd как нужно, если потребуется.

Я не думаю, что 2 будет ждать GC, отчасти потому, что GC не является "встроенным", а отчасти потому, что нет гарантии, что item является единственной ссылкой на экземпляр и требует GC в любом случае - как Я говорю, что счетчик ссылок будет уменьшен и что он.

Конечно, это просто мое чувство об этом, и мне будет интересно узнать, что другие скажут (я могу почувствовать несколько сообщений в блоге, возможно, в письме!)

Ответ 5

Когда я сохранил //do some stuff empty, обе версии генерировали точно такой же IL, поэтому там не может быть никакой возможной разницы. И я думаю, что если вы поместили там какой-то код, сгенерированный IL все равно будет таким же.

Что касается GC, второй пример может выглядеть так, как это может быть теоретически быстрее, потому что GC может собрать ListItem до выполнения приращения и сравнения цикла. Но на самом деле, он может сделать то же самое и в первом случае, поэтому нет никакой разницы.

Единственное исключение, когда две версии не эквивалентны, - это использование закрытий. В первой версии есть только одна переменная "экземпляр", которая закрыта, во втором случае их 32. Таким образом, существует разница в производительности (во втором случае объект закрытия создается для каждой итерации), но разница в значении гораздо важнее.

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