Iris Classon
Iris Classon - In Love with Code

‘Stupid’ Question 21: Is there a performance win with 'as' casting, and if so - does it matter?

[To celebrate my first year of programming I will ask a ‘stupid’ questions daily on my blog for a year, to make sure I learn at least 365 new things during my second year as a developer]

This is indeed a follow up question to question nr 20, ‘Stupid’ Question 20: How should I do casting in C#? Should I use the prefix cast or the as-cast?. As always one question sparks another one (or often several questions) - and this is the whole point for asking. With asking one question you will find all the other questions, and while you seek and find answer I hope it will spark that childlike curiosity we all had one day.

Is there a performance win with ‘as’ casting, and if so - does it matter?

So. Back to the question. Is ‘as’ faster than the (cast)? And if so, does it really matter today? The CLR and the computer hardware should fix that, right?
Turns out, that there is a performance win. ‘As’ checks to see if the object is of that type, and if not returns null and there is no exception. Exceptions carry a performance cost (but you might have another problem if you throw many exception), so ‘as’ will be slightly faster overall.
Does it matter?
I got the reply that it depends on how many iterations you would be doing, and if the answer to was ‘a lot’ then yes, it *could* matter, but the again also the discussion on exceptions and performance and what the real problem is.
Am I right here? This is such an interesting discussion, and I am so happy that so many devs joined in on the discussion!- keep it up guys and girls! Yey us!

Comments

Leave a comment below, or by email.
Yngve Bakken Nilsen
8/13/2012 9:39:03 AM
Excellent questions! I will answer my own thoughts later when I'm not on my iPad :) in the meantime, I suggest you check out Jon Skeets answer to the question on stackoverflow: http://stackoverflow.com/questions/496096/casting-vs-using-the-as-keyword-in-the-clr 

I think that answers actually both of your casting-posts :) 
Martin Liversage
8/13/2012 12:15:12 PM
I don't think this question is particular stupid. Anyway, the standard disclaimer for performance questions is that you really need to test your application in your environment and that you should avoid premature performance optimization.

Having said that, a cast and the "as" operator is compiled to different IL opcodes. A cast is compiled to castclass while the "as" operator is compiled to isinst.

If you search for these two opcodes you can find some benchmark results like an answer to the question Performance of TypeCasting on Stack Overflow. Comparing the performance of these two operations of course assume that the cast is possible without throwing an exception.

The somewhat surprising conclusion is that castclass is faster than isinst. Now this was his code on his machine so perhaps you will get another result in another environment.

In general I wouldn't be to concerned about performance when using either operator. A cast can throw an exception (which may be useful in some "correctness" scenarios) while the "as" operator can be used to branch the code depending on the type. Use the right operation for the right task.

Interestingly, code analysis has a performance rule for casts: CA1800: Do not cast unnecessarily. This can fire if you first use the "is" operator followed by the "as" operator resulting in two isinst opcodes being emitted. 
Anders Holmström
8/13/2012 1:03:33 PM
I find it's usually more important to ask "will this result in cleaner code?" than "will this result in faster code?". The performance win from this has to be negligent in all but the most extreme cases - and even then you probably wouldn't start optimizing by switching to "as"-casting only, as that would probably require more code in some other places instead.

If a null value is ok, use as. In general I'd say NullReferenceException is a lot uglier than casting exceptions, as the casting exception is less apt to happen at the location where the problem actually cropped up. 
Anders Holmström
8/13/2012 1:05:18 PM
Uhm: the casting exception is MORE apt to happen at the location where the problem actually cropped up. Do want comment editing... ;) 
Michael Lund (@iCodeIT_dk)
8/13/2012 1:30:46 PM
I tried microbenchmarking it.

100 million calls to a method making succesfull (cast) takes 1700ms
100 million calls to a method making succesfull as cast takes 1552ms
100 million calls to a method making unsuccesfull as cast takes 1984ms
100 (one hundred) calls to a method making unsuccesfull (cast) takes 617ms - extrapolating this gives 617000 seconds for 100 million calls.

My conclusion is that if you are absolutely sure about the cast you are doing will succeed, then it doesn't really matter. As Anders Holmström says: In this case do whatever looks cleaner.
If the cast can fail, there is no doubt: Use the as cast, because (cast) is about 300.000 times slower when it fails.

Ugly testcode below :-)

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace CastTest
{
  class Program
  {
    const int iter = 100;

    static void Main(string[] args)
    {
      var sw = new Stopwatch();
      sw.Start();
      for (int i = 0; i < iter; i++)
        Test1();
      sw.Stop();
      Console.WriteLine("Test 1 time: " + sw.ElapsedMilliseconds);

      sw.Restart();
      for (int i = 0; i < iter; i++)
        Test2();
      sw.Stop();
      Console.WriteLine("Test 2 time: " + sw.ElapsedMilliseconds);

      sw.Restart();
      for (int i = 0; i < iter; i++)
        Test3();
      sw.Stop();
      Console.WriteLine("Test 3 time: " + sw.ElapsedMilliseconds);

      sw.Restart();
      for (int i = 0; i < iter; i++)
        Test4();
      sw.Stop();
      Console.WriteLine("Test 4 time: " + sw.ElapsedMilliseconds);
      
      Console.ReadKey();
    }

    static void Test1()
    {
      var a = "123";
      try
      {
        int b = (int)(object)a;
      }
      catch (Exception)
      {
      }  
    }

    static void Test2()
    {
      var a = "123";
      try
      {
        object b = (object)a;
      }
      catch (Exception)
      {
      }  
      
    }

    static void Test3()
    {
      var a = "123";
      object b = a as object;
    }

    static void Test4()
    {
      var a = "123";
      var b = ((object)a) as List;
    }

  }
} 
Daniel Widegren
8/14/2012 12:26:37 AM
Well I can be wrong but right now there really isn't any preformence cost difference between theese two casts, just wrote a benchmark myself and I can't see any difference what so ever really anymore, I ran a test ittertating it over 50m times and the difference was ~1-2% with as casting being slower.

Code here: http://www.widegren.org/CastBench.cs

But as you can see this code, running it 50 million times takes about 1 second on a modern hardware :P this is really a totally NONE issisue right now. Yes the casting in this example only takes about ~2-4% of the time :D 
Daniel Widegren
8/14/2012 12:28:57 AM
Oh and I should add, that this test does not test the casting if exception occures, but fact is, the cast itself is sooo fast that it's a none issue, if you expect it to fail a lot I recommend as casting as you can handle it without using exceptions. But it's mostly a flavour of taste there to. 
Kristoffer Jansson
8/14/2012 9:59:08 AM
I remember reading on MSDN that a cast is quicker, but only if it won't throw an exception. The exception will of course slow it down, but that's a bug in you're code rather than a performance issue. If you're doing a cast that may not work, you better verify it beforehand using the 'is' operator.

As is equivalent to doing:

If(obj is string)
  Return (string)obj;
Else
  Return null;

If you're doing this, it doesn't matter if you're using cast or as. Use what makes the code easier to read. But if you're doing a cast that will definitely work every time, a simple cast would probably be a better pick. 
Joep Beusenberg
9/16/2012 2:47:40 AM
I know this question is old, but still.

After optimization there will hardly be a performance difference. It's actually all about semantics.
The (cast) operator implies you expect or know the object to be of the target type. On the other hand, the as operation implies you assume it might be of the target type, but might as well be not.
When you expect a value to be of a certain type, you don't want to try and debug the NullReferenceException that the as operator will cause later on, after the fact.
And if you know the object might not be that type, you don't want to be bothered by InvalidCastExceptions. 


Last modified on 2012-08-12

comments powered by Disqus