views:

176

answers:

2

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 :)

+6  A: 
  • 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.

Reed Copsey
Interesting, I thought it would show all of the nested IL calls, thanks for the great answer! As a side question, do you know where I can learn more about IL or if I should care too much about it? Sorry, I know those are kind of involved questions
Allen
To really understand IL, start reading up on compiler theory and/or assembly language. It helps a ton to figure out what it's doing. Otherwise, MSDN's sections on Reflection.Emit have a lot of good info.
Reed Copsey
+3  A: 

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.

Franci Penov