I have a program that requires fast performance. Within one of its inner loops, I need to test the type of an object to see whether it inherits from a certain interface.
One way to do this would be with the CLR's built-in type-checking functionality. The most elegant method there probably being the 'is' keyword:
if (obj is ISpecialType)
Another approach would be to give the base class my own virtual GetType() function which returns a pre-defined enum value (in my case, actually, i only need a bool). That method would be fast, but less elegant.
I have heard that there is an IL instruction specifically for the 'is' keyword, but that doesn't mean it executes fast when translated into native assembly. Can anyone share some insight into the performance of 'is' versus the other method?
UPDATE: Thanks for all the informed answers! It seem a couple helpful points are spread out among the answers: Andrew's point about 'is' automatically performing a cast is essential, but the performance data gathered by Binary Worrier and Ian is also extremely useful. It would be great if one of the answers were edited to include all of this information.
Point Andrew Hare made about performance lost when you perform
is
check and then cast was valid but in C# 7.0 we can do is check witch pattern match to avoid additional cast later on:Further more if you need to check between multiple types C# 7.0 pattern matching constructs now allow you to do
switch
on types:You can read more about pattern matching in C# in documentation here.
Andrew is correct. In fact with code analysis this gets reported by Visual Studio as an unnecessary cast.
One idea (without knowing what you're doing is a bit of a shot in the dark), but I've always been advised to avoid checking like this, and instead have another class. So rather than doing some checks and having different actions depending on the type, make the class know how to process itself...
e.g. Obj can be ISpecialType or IType;
both of them have a DoStuff() method defined. For IType it can just return or do custom stuff, whereas ISpecialType can do other stuff.
This then completely removes any casting, makes the code cleaner and easier to maintain, and the class knows how to do it's own tasks.
In case anyone is wondering, I've made tests in Unity engine 2017.1, with scripting runtime version .NET4.6(Experimantal) on a notebook with i5-4200U CPU. Results:
Average Relative To Local Call LocalCall 117.33 1.00 is 241.67 2.06 Enum 139.33 1.19 VCall 294.33 2.51 GetType 276.00 2.35
Full article: http://www.ennoble-studios.com/tuts/unity-c-performance-comparison-is-vs-enum-vs-virtual-call.html
I did a performance comparsion on two possibilities of type comparison
The result is: Using "is" is about 10x faster !!!
Output:
Time for Type-Comparison: 00:00:00.456
Time for Is-Comparison: 00:00:00.042
My Code:
Ok so I was chatting about this with someone and decided to test this more. As far as I can tell, the performance of
as
andis
are both very good, compared to testing your own member or function to store type information.I used
Stopwatch
, which I just learned may not be the most reliable approach, so I also triedUtcNow
. Later, I also tried Processor time approach which seems similar toUtcNow
including unpredictable create times. I also tried making the base class non-abstract with no virtuals but it didn't seem to have a significant effect.I ran this on a Quad Q6600 with 16GB RAM. Even with 50mil iterations, the numbers still bounce around +/- 50 or so millisec so I wouldn't read too much into the minor differences.
It was interesting to see that x64 created faster but executed as/is slower than x86
x64 Release Mode:
Stopwatch:
As: 561ms
Is: 597ms
Base property: 539ms
Base field: 555ms
Base RO field: 552ms
Virtual GetEnumType() test: 556ms
Virtual IsB() test: 588ms
Create Time : 10416ms
UtcNow:
As: 499ms
Is: 532ms
Base property: 479ms
Base field: 502ms
Base RO field: 491ms
Virtual GetEnumType(): 502ms
Virtual bool IsB(): 522ms
Create Time : 285ms (This number seems unreliable with UtcNow. I also get 109ms and 806ms.)
x86 Release Mode:
Stopwatch:
As: 391ms
Is: 423ms
Base property: 369ms
Base field: 321ms
Base RO field: 339ms
Virtual GetEnumType() test: 361ms
Virtual IsB() test: 365ms
Create Time : 14106ms
UtcNow:
As: 348ms
Is: 375ms
Base property: 329ms
Base field: 286ms
Base RO field: 309ms
Virtual GetEnumType(): 321ms
Virtual bool IsB(): 332ms
Create Time : 544ms (This number seems unreliable with UtcNow.)
Here's most of the code:
I'm with Ian, you probably don't want to do this.
However, just so you know, there is very little difference between the two, over 10,000,000 iterations
I personally wouldn't fix this problem this way, but if I was forced to pick one method it would be the built in IS check, the performance difference isn't worth considering the coding overhead.
My base and derived classes
JubJub: As requested more info on the tests.
I ran both tests from a console app (a debug build) each test looks like the following
Running in release, I get a difference of 60 - 70 ms, like Ian.
Further Update - Oct 25th 2012
After a couple of years away I noticed something about this, the compiler can choose to omit
bool b = a is MyClassB
in release because b isn't used anywhere.This code . . .
. . . consistently shows the
is
check coming in at approx 57 milliseconds, and the enum comparison coming in at 29 milliseconds.NB I'd still prefer the
is
check, the difference is too small to care about