Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I compare IL code to determine which technique is faster or better?

Background

This question got me thinking about something. Lately, since I've been looking at linq pad's IL functionality, I've been comparing the IL code of two approaches to the same problem to "determine" which is best.

Using the question linked to above, about converting an array, I generated the IL code for the two answers:

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

produced:

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         

and the other answer:

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

produced:

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     

Looking at this, all I can tell is that the latter option

  • takes 1 extra line
  • uses linq when the 1st answer doesn't
  • creates the Int's differently via IL_0039.

Questions

  • For this specific example, are my assumptions correct?
  • In general, how should I go about comparing two solutions via IL code?
  • In general, does a solution with fewer IL LOC mean that it will be faster or use less memory?
  • As the title says, Can I compare IL code to determine which technique is faster or better?

FWIW, I don't do this often, just every once in a rare while when some discussion comes up amongst developers at work. Someone will say "oh this is more efficient" and we'll throw it into linqpad to check out the IL code. Also FWIW, I almost always abide by the getting it working before getting it efficient/fast approach. Just so people don't think I'm constantly comparing IL code of what I'm developing :)

like image 238
Allen Rice Avatar asked Aug 19 '09 00:08

Allen Rice


People also ask

What is the benefit of compiling in to IL code?

In addition to SLaks's answer, compiling to IL enables a degree of cross-language interoperability that generally doesn't exist in interpreted languages. This advantage can be huge for new languages.

Can you view il code?

Make sure that IL is selected on the IL Viewer toolbar. dotPeek will display the IL code corresponding to the symbol in the IL Viewer window.

What is IL code?

What is IL? Languages such as C# or Java are not directly compiled into machine instructions, but another intermediate code. For C# this intermediate code is called Common Intermediate Language (CIL, or just IL). Most Portable Executable (PE) files compiled and assembled from C#, whether .


2 Answers

  • For this specific example, are my assumptions correct?
  • In general, how should I go about comparing two solutions via IL code?
  • In general, does a solution with fewer IL LOC mean that it will be faster or use less memory?
  • As the title says, Can I compare IL code to determine which technique is faster or better?

1) Your assumptions are correct about what's happening.

2) You need to understand what the IL code is doing in order to determine which is "better"

3) No. It means it takes fewer instructions to run. However, these individual instructions might use more memory or less. For example, the instruction you were referencing, in one case is creating a Func delegate, and in the other is creating a Converter object. Without more information, it's difficult to tell which of those two things is more expensive.

4) Yes and no....

The problem is that the IL code will tell you what's happening, but it's really the nested calls in IL that are going to be the large performance driver. If the IL code is doing simple operations everywhere, in general the shorter the better (although individual IL operations can vary in speed, themselves). When the code calls into methods or constructors on other types, such as yours, this becomes impossible to tell from this alone. One line of IL can take longer in one case (if it's calling an expensive method, for example) than 50 in another case (where they're doing simple operations).

In your case above, for example, the first 20 operations are very, very fast, wheras the last few take nearly all of your executable time.

like image 160
Reed Copsey Avatar answered Oct 13 '22 02:10

Reed Copsey


The chunk of the work for both answers is done at IL 004A (and IL 004F for the second one). Unless you know the cost of these external calls, there's no practical basis on which you could compare the performance of the two answers.

like image 42
Franci Penov Avatar answered Oct 13 '22 02:10

Franci Penov