locked
Casting Performance RRS feed

  • Question

  • Let's say I have an object that needs to be cast before passing it to two different functions.  Is there a performance hit if I cast the object twice, once in each function parameter versus casting it once to a variable and simple passing that variable to the functions?

    For example, given object o which would be better:

     

    Function1((T)o);
    Function2((T)o);

     

    or

     

    T temp = (T)o;
    Function1(temp);
    Function2(temp);

     

    I like how compact and concise the first version is but worry that I could be introducing inefficiencies into my code.

     

     



    Friday, June 24, 2011 2:21 PM

Answers

  • I've compiled a sample program with compiler optimizations and without.

     

    using System;
    
    
    class A
    {
    
     public static int Function(int x)
     {
      return x+1;
     }
    
     public static void Main()
     {
      object a = 1;
    
      Function((int)a);
      Function((int)a);
    
     }
    
    }
    

     

    In both cases unboxing will occur:

    .method public hidebysig static void Main() cil managed
    {
     .entrypoint
     // Code size    32 (0x20)
     .maxstack 1
     .locals init (object V_0)
     IL_0000: ldc.i4.1
     IL_0001: box    [mscorlib]System.Int32
     IL_0006: stloc.0
     IL_0007: ldloc.0
     IL_0008: unbox.any [mscorlib]System.Int32
     IL_000d: call    int32 A::Function(int32)
     IL_0012: pop
     IL_0013: ldloc.0
     IL_0014: unbox.any [mscorlib]System.Int32
     IL_0019: call    int32 A::Function(int32)
     IL_001e: pop
     IL_001f: ret
    } // end of method A::Main
    

    And with reference types (conversion from object to string) castclass intstruction will be generated:

     

    <strong> IL_0014: castclass [mscorlib]System.String</strong>
     IL_0019: call    void A::Function(string)
     IL_001e: nop
     IL_001f: ldloc.0
    <strong> IL_0020: castclass [mscorlib]System.String</strong>
     IL_0025: call    void A::Function(string)
    

    If I run this program using different types (and different conversion types boxing, reference type cast, no cast) in loop with  10 000 000 iterations, i'l get following numbers:

     

     With unboxing: 9 ms

         Function((int)a); Function((int)a);

     With conversion from int to long: 9 ms
         Function((long)a); Function((long)a);

     With conversion from int to double: 9 ms

         Function((double)a); Function((double)a);

     With conversion from one reference type to another reference type: 31 ms

         Function((string)a); Function((string)a);

    With single conversion from one reference type to another reference type: ~15 ms
         string s = a as string;
         Function(s);
         Function(s);
    With single conversion and null comparison from one reference type to another reference type: ~23 ms
         string s = a as string;
         if (s!=null){
           Function(s);
           Function(s);
         }
    Without conversion, reference type (string): 14 ms:

         Function(a); Function(a);

    Without conversion, value type (int): 9 ms:

         Function(a); Function(a);

     

    So, in general I would conclude that reference type casting is more expensive than unboxing. But if your loop has relatively small amount of iterations (< 1000000) and if cost of function execution is quite significant you might safelly pick whatever option you like more.

     


    Please remember to mark the replies as answers if they help
    • Proposed as answer by Min Zhu Tuesday, June 28, 2011 2:51 AM
    • Marked as answer by Min Zhu Thursday, June 30, 2011 2:50 AM
    Saturday, June 25, 2011 7:24 PM
  • Unnecessary casting is a performance warning when you enable static Code Analysis.
    Casting is expesnive.
    Another corrollary situation is:
    if (variable is MyType)
    {
     MyType typedVariable = (MyType)variable;
    }
    
    This block of code is why the as operator was invented.
    The correct way to write this is:
    MyType typedVariable = variable as MyType;
    if (typedVariable != null)
    {
    }
    

    Every programmer prefers the first block - using the is operator and casting - because it reads easier.  But it's a double cast and code analysis will warn you about it because casting is very expensive.
    Evan
    • Proposed as answer by Min Zhu Tuesday, June 28, 2011 2:51 AM
    • Marked as answer by Min Zhu Thursday, June 30, 2011 2:50 AM
    Sunday, June 26, 2011 3:26 AM

All replies

  • Hi, 

    Here is the nice article on performance analysis of primitive type casting, down casting and up casting... 

    http://www.codeproject.com/KB/cs/csharpcasts.aspx


    If this post answers your question, please click "Mark As Answer". If this post is helpful please click "Mark as Helpful".
    Friday, June 24, 2011 2:35 PM
  • Unless you are doing it in a really, really tight loop you probably won't notice the difference.  I think though if you are going to need it twice like that I'd do it once and use the result multiple times.  I typically prefer the 'as' cast  as the article Kris444 points out mentions and then check for null on failure to cast.

     

    var temp = o as T;
    
    if (temp != null)
    {
        Function1(temp);
        Function2(temp);
    }
    

     


    James Michael Hare

    Blog: http://www.geekswithblogs.net/BlackRabbitCoder

    Twitter: @BlkRabbitCoder

    There are 10 kinds of people in the world: those who know binary and those who don't...

    Friday, June 24, 2011 3:07 PM
  • I've compiled a sample program with compiler optimizations and without.

     

    using System;
    
    
    class A
    {
    
     public static int Function(int x)
     {
      return x+1;
     }
    
     public static void Main()
     {
      object a = 1;
    
      Function((int)a);
      Function((int)a);
    
     }
    
    }
    

     

    In both cases unboxing will occur:

    .method public hidebysig static void Main() cil managed
    {
     .entrypoint
     // Code size    32 (0x20)
     .maxstack 1
     .locals init (object V_0)
     IL_0000: ldc.i4.1
     IL_0001: box    [mscorlib]System.Int32
     IL_0006: stloc.0
     IL_0007: ldloc.0
     IL_0008: unbox.any [mscorlib]System.Int32
     IL_000d: call    int32 A::Function(int32)
     IL_0012: pop
     IL_0013: ldloc.0
     IL_0014: unbox.any [mscorlib]System.Int32
     IL_0019: call    int32 A::Function(int32)
     IL_001e: pop
     IL_001f: ret
    } // end of method A::Main
    

    And with reference types (conversion from object to string) castclass intstruction will be generated:

     

    <strong> IL_0014: castclass [mscorlib]System.String</strong>
     IL_0019: call    void A::Function(string)
     IL_001e: nop
     IL_001f: ldloc.0
    <strong> IL_0020: castclass [mscorlib]System.String</strong>
     IL_0025: call    void A::Function(string)
    

    If I run this program using different types (and different conversion types boxing, reference type cast, no cast) in loop with  10 000 000 iterations, i'l get following numbers:

     

     With unboxing: 9 ms

         Function((int)a); Function((int)a);

     With conversion from int to long: 9 ms
         Function((long)a); Function((long)a);

     With conversion from int to double: 9 ms

         Function((double)a); Function((double)a);

     With conversion from one reference type to another reference type: 31 ms

         Function((string)a); Function((string)a);

    With single conversion from one reference type to another reference type: ~15 ms
         string s = a as string;
         Function(s);
         Function(s);
    With single conversion and null comparison from one reference type to another reference type: ~23 ms
         string s = a as string;
         if (s!=null){
           Function(s);
           Function(s);
         }
    Without conversion, reference type (string): 14 ms:

         Function(a); Function(a);

    Without conversion, value type (int): 9 ms:

         Function(a); Function(a);

     

    So, in general I would conclude that reference type casting is more expensive than unboxing. But if your loop has relatively small amount of iterations (< 1000000) and if cost of function execution is quite significant you might safelly pick whatever option you like more.

     


    Please remember to mark the replies as answers if they help
    • Proposed as answer by Min Zhu Tuesday, June 28, 2011 2:51 AM
    • Marked as answer by Min Zhu Thursday, June 30, 2011 2:50 AM
    Saturday, June 25, 2011 7:24 PM
  • Unnecessary casting is a performance warning when you enable static Code Analysis.
    Casting is expesnive.
    Another corrollary situation is:
    if (variable is MyType)
    {
     MyType typedVariable = (MyType)variable;
    }
    
    This block of code is why the as operator was invented.
    The correct way to write this is:
    MyType typedVariable = variable as MyType;
    if (typedVariable != null)
    {
    }
    

    Every programmer prefers the first block - using the is operator and casting - because it reads easier.  But it's a double cast and code analysis will warn you about it because casting is very expensive.
    Evan
    • Proposed as answer by Min Zhu Tuesday, June 28, 2011 2:51 AM
    • Marked as answer by Min Zhu Thursday, June 30, 2011 2:50 AM
    Sunday, June 26, 2011 3:26 AM