Один из них использует больше ресурсов, чем другой? - PullRequest
11 голосов
/ 03 августа 2011

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

Я думаю, что 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
}

Ответы [ 5 ]

10 голосов
/ 03 августа 2011

Я скопировал ваш код в 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

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

4 голосов
/ 03 августа 2011

Это зависит от того, что означает «// do some stuff».

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

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

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

3 голосов
/ 03 августа 2011

Удивительно, но мои тесты показывают, что пример 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
2 голосов
/ 03 августа 2011

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

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

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

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

1 голос
/ 03 августа 2011

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...