Могу ли я сравнить IL-код, чтобы определить, какая техника быстрее или лучше?

Фон

Этот вопрос заставил меня задуматься. В последнее время, поскольку я смотрел на linq pad Функциональность IL, я сравнивал код IL два подхода к одной и той же проблеме, чтобы "определить", что лучше всего.

Используя вопрос, связанный с выше, о преобразовании массива, я сгенерировал IL-код для двух ответов:

var arr = new string[] { "1", "2", "3", "4" };
var result = Array.ConvertAll(arr, s => Int32.Parse(s));

производится:

IL_0001:  ldc.i4.4    
IL_0002:  newarr      System.String
IL_0007:  stloc.2     
IL_0008:  ldloc.2     
IL_0009:  ldc.i4.0    
IL_000A:  ldstr       "1"
IL_000F:  stelem.ref  
IL_0010:  ldloc.2     
IL_0011:  ldc.i4.1    
IL_0012:  ldstr       "2"
IL_0017:  stelem.ref  
IL_0018:  ldloc.2     
IL_0019:  ldc.i4.2    
IL_001A:  ldstr       "3"
IL_001F:  stelem.ref  
IL_0020:  ldloc.2     
IL_0021:  ldc.i4.3    
IL_0022:  ldstr       "4"
IL_0027:  stelem.ref  
IL_0028:  ldloc.2     
IL_0029:  stloc.0     
IL_002A:  ldloc.0     
IL_002B:  ldsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_0030:  brtrue.s    IL_0045
IL_0032:  ldnull      
IL_0033:  ldftn       b__0
IL_0039:  newobj      System.Converter<System.String,System.Int32>..ctor
IL_003E:  stsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_0043:  br.s        IL_0045
IL_0045:  ldsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_004A:  call        System.Array.ConvertAll
IL_004F:  stloc.1     

b__0:
IL_0000:  ldarg.0     
IL_0001:  call        System.Int32.Parse
IL_0006:  stloc.0     
IL_0007:  br.s        IL_0009
IL_0009:  ldloc.0     
IL_000A:  ret         

а другой ответ:

var arr = new string[] { "1", "2", "3", "4" };
var result = arr.Select(s => int.Parse(s)).ToArray();

производится:

IL_0001:  ldc.i4.4    
IL_0002:  newarr      System.String
IL_0007:  stloc.2     
IL_0008:  ldloc.2     
IL_0009:  ldc.i4.0    
IL_000A:  ldstr       "1"
IL_000F:  stelem.ref  
IL_0010:  ldloc.2     
IL_0011:  ldc.i4.1    
IL_0012:  ldstr       "2"
IL_0017:  stelem.ref  
IL_0018:  ldloc.2     
IL_0019:  ldc.i4.2    
IL_001A:  ldstr       "3"
IL_001F:  stelem.ref  
IL_0020:  ldloc.2     
IL_0021:  ldc.i4.3    
IL_0022:  ldstr       "4"
IL_0027:  stelem.ref  
IL_0028:  ldloc.2     
IL_0029:  stloc.0     
IL_002A:  ldloc.0     
IL_002B:  ldsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_0030:  brtrue.s    IL_0045
IL_0032:  ldnull      
IL_0033:  ldftn       b__0
IL_0039:  newobj      System.Func<System.String,System.Int32>..ctor
IL_003E:  stsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_0043:  br.s        IL_0045
IL_0045:  ldsfld      UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_004A:  call        System.Linq.Enumerable.Select
IL_004F:  call        System.Linq.Enumerable.ToArray
IL_0054:  stloc.1     

b__0:
IL_0000:  ldarg.0     
IL_0001:  call        System.Int32.Parse
IL_0006:  stloc.0     
IL_0007:  br.s        IL_0009
IL_0009:  ldloc.0     
IL_000A:  ret     

Глядя на это, все, что я могу сказать, это то, что последний вариант

  • занимает 1 дополнительную строку
  • использует linq, когда 1-й ответ не
  • создает Int по-разному через IL_0039.

Вопросы

  • В этом конкретном примере правильны ли мои предположения?
  • В общем, как мне следует сравнивать два решения с помощью IL-кода?
  • В общем, решение с меньшим количеством IL LOC означает, что оно будет быстрее или будет использовать меньше памяти?
  • Как говорится в названии, могу ли я сравнить IL-код, чтобы определить, какой метод быстрее или лучше?

FWIW, я не делаю этого часто, каждый раз в редких случаях, когда некоторые обсуждения появляются среди разработчиков на работе. Кто-то скажет: "О, это более эффективно", и мы бросим его в linqpad, чтобы проверить код IL. Кроме того, FWIW, я почти всегда придерживаюсь того, чтобы заставить его работать, прежде чем сделать его эффективным/быстрым. Просто люди не думают, что я постоянно сравниваю IL-код того, что я разрабатываю:)

Ответ 1

  • В этом конкретном примере правильны ли мои предположения?
  • В общем, как мне следует сравнивать два решения с помощью IL-кода?
  • В общем, решение с меньшим количеством IL LOC означает, что оно будет быстрее или будет использовать меньше памяти?
  • Как говорится в названии, могу ли я сравнить IL-код, чтобы определить, какой метод быстрее или лучше?

1) Ваши предположения верны в отношении того, что происходит.

2) Вам нужно понять, что делает код IL, чтобы определить, что "лучше"

3) Нет. Это означает, что для запуска требуется меньшее количество инструкций. Однако эти индивидуальные инструкции могут использовать больше памяти или меньше. Например, команда, на которую вы ссылались, в одном случае создает делегат Func, а другой создает объект Converter. Без дополнительной информации трудно сказать, какая из этих двух вещей дороже.

4) Да и нет....

Проблема в том, что код IL скажет вам, что происходит, но это действительно вложенные вызовы в IL, которые будут большим драйвером производительности. Если код IL выполняет простые операции повсюду, в целом, чем короче, тем лучше (хотя отдельные операции ИИ могут варьироваться в зависимости от скорости). Когда код вызывает методы или конструкторы на других типах, таких как ваши, это становится невозможным из всего этого. Одна строка IL может занять больше времени в одном случае (если она вызывает дорогой метод, например), чем 50 в другом случае (где они выполняют простые операции).

В приведенном выше примере, например, первые 20 операций очень, очень быстрые, если последние несколько принимают почти все ваши исполняемые файлы.

Ответ 2

Часть работы для обоих ответов выполняется в IL 004A (и IL 004F для второго). Если вы не знаете стоимость этих внешних вызовов, нет никакой практической основы, на которой вы могли бы сравнить производительность двух ответов.