Click here to Skip to main content
15,867,330 members
Articles / Containers / Virtual Machine
Article

Type casting impact over execution performance in C#

Rate me:
Please Sign up or sign in to vote.
4.71/5 (32 votes)
21 Aug 20045 min read 259.3K   35   28
This article analyzes the most common type casting situations in C# and the compiler behaviour in them.

Introduction

Explicit and implicit type casting is a common programming topic for almost any imperative programming language. Most C, C++, or Pascal programmers care about efficiency and speed of their code; but those who use managed programming environments, such as Java, Visual Basic, or C# rely all the optimizing tasks on the compiler and the runtime environment.

This can be a good approach in many cases, but managed languages are becoming more and more popular also for high-performance applications where the knowledge of the language, compiler, and runtime environment can enhance a program's quality and speed.

This article analyzes the most common type casting situations and the compiler behavior in them. We are going to study the MSIL generated code, but not the machine-specific instruction sequences due to the implementation and vendor dependency.

Casting primitive types

Primitive types are those non-composed types which can be handled directly by the (virtual) machine instructions, i.e., int, long, float, etc... Those types doesn't have inner structure, and are always passed by value if the programmer doesn't specify explicitly other behavior (using the out and ref modifiers). Let's see a simple example about using and casting primitive types:

C#
int z = 10;
double r = 3.4;
uint n = 20;

r = z; // Implicit conversion from int to double (1)
z = (int)r; // Explicit conversion from double to int (2)
n = (uint)z; // Explicit conversion from int to uint (3)

This sample performs some conversions in the set of primitive types, leaving in some cases the casting tasks to the compiler and marking conversions explicitly in some other cases.

OK, time to dive into the MSIL generated code and check the impact of type casts in our code:

MSIL
.locals init ([0] int32 z,
          [1] float32 r,
          [2] unsigned int32 n)
 IL_0000:  ldc.i4.s   10
 IL_0002:  stloc.0
 IL_0003:  ldc.r4     (9A 99 59 40)
 IL_0008:  stloc.1
 IL_0009:  ldc.i4.s   20
 IL_000b:  stloc.2 //(1)
 IL_000c:  ldloc.0
 IL_000d:  conv.r4
 IL_000e:  stloc.1
 IL_000f:  ldloc.1 //(2)
 IL_0010:  conv.i4
 IL_0011:  stloc.0
 IL_0012:  ldloc.0 //(3)
 IL_0013:  stloc.2
 IL_0014:  ret

As we can see, there are several Conv.XY instructions in the code, whose function is to convert the value at the top of the stack to the type designed in the opcode (r4, i4, etc...). From now, we know that the "innocent" explicit and implicit conversions between primitive types generate instructions which can be avoided with a consistent type usage. The same conversions are applied in 64-bit data types, such as double, long and ulong.

Note that the last type cast doesn't need an explicit "Conv" opcode due to the nature of the involved types: int and uint; these types have a very close storage structure (big endian bit order with a sign bit in the signed type) and conversion sign issues must be controlled by the programmer.

A special kind of primitive type is bool (handled internally as an int), whose conversions to numeric types (and backward) are not allowed in C#, so we will not study them.

Downcasting object references

C# provides two ways for casting object references (note that all types, unless those studied in the previous section, are reference types):

C#
object myClass = new MyClass();

((MyClass)myClass).DoSome(); //(1)
(myClass as MyClass).DoSome(); //(2)</CODE>

The previous is a good example of downcasting (casting from the top to the bottom of the class hierarchy). The method used to perform the cast appears to be the same, but the generated MSIL sequences are a bit different:

MSIL
  .locals init ([0] object myClass)
  IL_0000:  newobj     instance void Sample.MyClass::.ctor()
IL_0005:  stloc.0
IL_0006:  ldloc.0 //(1)
IL_0007:  castclass  Sample.MyClass
IL_000c:  callvirt   instance void Sample.MyClass::DoSome()
IL_0011:  ldloc.0 //(2)
IL_0012:  isinst     Sample.MyClass
IL_0017:  callvirt   instance void Sample.MyClass::DoSome()
IL_001c:  ret

In the first line of code, the compiler emits a "Castclass" opcode, which converts the reference to the type specified between the parenthesis if possible (if not, an InvalidCastException exception is thrown).

In the second case, the as operator is translated as an "IsInst" opcode, which works much faster, because it only checks the reference type but doesn't perform any sort of cast (nor throws any exception).

In performance terms, we prefer the second option, because the "IsInst" speeds up much more the code execution, avoiding type casts and exception throwing. Here is a sample of the speed increment obtained using the "as" operator:

Image 1

In the other hand, parenthesized casts give a better error control to programmers, avoiding the null-reference errors obtained when invalid typecasts happen using the "as" operator.

Upcasting object references

Let's make the opposite! Now it's time for climbing up into the class hierarchy, and see how slow (or fast) are these sort of casts. The following example creates an object of the type MyDerivedClass and stores its reference in a MyClass type variable:

C#
MyDerivedClass myDerivedClass = new MyDerivedClass();
MyClass myClass = myDerivedClass;

And the produced code is:

MSIL
.locals init ([0] class Sample.MyDerivedClass myDerivedClass,
         [1] class Sample.MyClass myClass)
IL_0000:  newobj     instance void Sample.MyDerivedClass::.ctor()
IL_0005:  stloc.0
IL_0006:  ldloc.0
IL_0007:  stloc.1
IL_0008:  ret

As we can see, there are no conversion opcodes, just reference loading and storing. This is good for out efficiency purposes... as expected, upcasting type checks are made at compile time and the runtime costs are as cheap as a simple assign between variables of the same type.

Casting operators

C# language contains a great feature which allows to define implicit and explicit conversion operators. The efficiency of these casting methods depends on the casting method implementation. Anyway, these functions are always static and have only one parameter, so the procedure call overhead is small (no "this" parameter should be passed). Anyway, it seems to be that the Microsoft C# compiler doesn't inline those methods, so arranging parameters and return addresses in the stack may slow your code execution speed.

Putting it all together

Here are some general tips for optimizing your programs based on the results obtained in the previous sections:

  • Numeric type conversions are usually expensive, take them out of the loops and recursive functions and use the same numeric types when possible.
  • Downcasting is a great invention but the type checks involved have a great impact on execution performance, check the object types out of loops and recursive functions, and use the "as" operator into them.
  • Upcasting is cheap!, use it everywhere you need.
  • Build lightweight conversion operators to make custom casts faster.

Tools used

All the tests and disassemblies have been made using the tools included in the .NET Framework SDK. ILDasm can tell you much about your program's performance flaws, so play with it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Instructor/Trainer
Netherlands Netherlands
Emilio is a Computer Engineer currently working as software engineer in embedded systems.

Main interests are C/C++ programming, algorithmics, compilers, embedded systems, cryptography, and operating systems.

Comments and Discussions

 
GeneralMy vote of 5 Pin
7flare2-Nov-17 23:12
7flare2-Nov-17 23:12 
SuggestionSomething too keep in mind when using "as" (and also "is" for type checking)... Pin
Paul Easter21-Aug-14 5:10
Paul Easter21-Aug-14 5:10 
GeneralYour measurement is incorrect Pin
ATPIncorporation3-Jun-14 18:14
ATPIncorporation3-Jun-14 18:14 
GeneralMy vote of 4 Pin
chapmb_00125-Jul-13 16:27
chapmb_00125-Jul-13 16:27 
GeneralMy vote of 5 Pin
adayler23-May-13 8:20
adayler23-May-13 8:20 
GeneralMy vote of 5 Pin
adayler14-Nov-12 11:30
adayler14-Nov-12 11:30 
QuestionSo the final conclusion is ?. Pin
LiorCohen25-Mar-12 1:28
LiorCohen25-Mar-12 1:28 
GeneralMy vote of 1 Pin
tartaros4-Jan-11 22:13
tartaros4-Jan-11 22:13 
General[My vote of 1] My Vote of 1 - **This article is incorrect** Pin
Jonathan C Dickinson20-Aug-10 0:18
Jonathan C Dickinson20-Aug-10 0:18 
GeneralPerformance Pin
Phil24-Nov-09 3:49
Phil24-Nov-09 3:49 
Generaltypecasting Pin
christefer1-Nov-07 5:06
christefer1-Nov-07 5:06 
hi iam facing the problem with type casting while converting the following code
intReq = Convert.ToInt32 (selectRow.Cells[0].ToString());
iam getting the error as
Input string was not in a correct format.
how to rectify it
GeneralRe: typecasting Pin
sh3llc0de10-Mar-08 7:23
sh3llc0de10-Mar-08 7:23 
GeneralI don't get it Pin
Bullno14-Oct-07 3:00
Bullno14-Oct-07 3:00 
QuestionHow about some Orcas benchmarks? Pin
UL-Tomten25-Sep-07 21:55
UL-Tomten25-Sep-07 21:55 
GeneralCast with () versus "as" PinPopular
snarfblam27-Feb-07 14:19
snarfblam27-Feb-07 14:19 
GeneralCast is faster than as in .net 2.0 Pin
linbin6816-Nov-06 10:45
linbin6816-Nov-06 10:45 
AnswerRe: Cast is faster than as in .net 2.0 Pin
UL-Tomten25-Sep-07 21:51
UL-Tomten25-Sep-07 21:51 
QuestionSample Code? Pin
Anonymous16-Sep-05 4:54
Anonymous16-Sep-05 4:54 
GeneralSome more info Pin
leppie22-Aug-04 1:32
leppie22-Aug-04 1:32 
GeneralRe: Some more info Pin
Emilio Guijarro22-Aug-04 2:46
Emilio Guijarro22-Aug-04 2:46 
GeneralRe: Some more info Pin
leppie22-Aug-04 3:40
leppie22-Aug-04 3:40 
GeneralRe: Some more info Pin
Sebastien Lorion22-Aug-04 13:21
Sebastien Lorion22-Aug-04 13:21 
GeneralRe: Some more info Pin
leppie22-Aug-04 13:36
leppie22-Aug-04 13:36 
GeneralRe: Some more info Pin
Anonymous22-Aug-04 13:41
Anonymous22-Aug-04 13:41 
NewsRe: Some more info Pin
UL-Tomten25-Sep-07 21:53
UL-Tomten25-Sep-07 21:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.