C# · CodeProject · Dotnet

Internals of Conversion styles in .NET

Dear Reader,

In this post i would like to share some things about Conversion styles in .NET i learned few days back. Basically in this case I am talking about casting any numeric value to an equivalent char type.

There are three ways one can convert one type to another, in this case numeric to char:

  1. Direct Casting
  2. Via Convert helper class
  3. Via IConvertible interface

Lets dig a bit deeper into each of these styles and see its pros and cons if any.

Direct Casting:

This conversion style is the most basic and most efficient one. In this style, the compiler need not to emit any special OpCodes or method calls. Instead it just emits basic OpCodes which does the job efficiently. Basically compiler emits conv.u2 or conv.ovf.u2 OpCode instructions for unchecked and checked operations respectively.

Lets see the IL for this operation:

int value = 99;
char c = (char)value;

For the above code, the IL generated is:

IL_0000: nop
IL_0001: ldc.i4.s 99
IL_0003: stloc.0
IL_0004: nop
IL_0005: ldloc.0
IL_0006: conv.u2
IL_0007: stloc.1
IL_0008: nop
IL_0009: ret

As you can see, there is no extra complex OpCodes being generated nor any method calls. Only opcode which does the actual conversion is generated at IL_0006 line.

Although this style is most efficient one, the only draw back of using this style is that it does not support culture specific conversion in other words you can’t specify IFormatProvider to the conversion operation.

Convert Helper class:

In this style we use Convert class which is a helper class provided by .NET framework class library (FCL).  As you may already know that Convert class is a static class which provides alot of static methods for conversion operations. Since there is a method call in this scenario, so this is considered to be the second most efficient style among others. The Convert methods internally checks for overflow operation conditions by default. Hence when ever there is over flow occurrence, it throws an exception of type OverFlowException.

So in this case, i have used Convert.ToChar(Int32) API. So the code in FCL for this API looks like as shown below:

public static char ToChar(int value)
{
if (value < 0 || value > 65535)
{
throw new OverflowException(
   Environment.GetResourceString("Overflow_Char"));
}
return (char)value;
}

One important point worth remembering here is that, one needs to be careful while using the API Convert.ToChar(Object, IFormatProvider) with valuetypes. Because internally it uses IConvertible interface for conversion which results in low performance issues as explained in the next section.

IConvertible Interface:

In BCL, many basic types viz Int32, DateTime, Char, etc. does implements interface IConvertible. Many other types might as well implement this interface, but it’s of no interest here. One important thing to remember here is that this style of conversion is very poor in performance, since in case of valuetypes the input is first boxed and then conversion is done on it.

Lets see a sample example:

char c = ((IConvertible)95).ToChar(null);

In the above line, Explicit conversion to IConvertible is required, because many types in BCL viz Char, String, Int32, etc. do implements IConvertible methods explicitly.  Looking at the IL for the above code shows the exact problem:

IL_0000: nop
IL_0001: ldc.i4.s 95
IL_0003: box [mscorlib]System.Int32
IL_0008: ldnull
IL_0009: callvirt instance char [mscorlib]System.IConvertible::ToChar(class [mscorlib]System.IFormatProvider)
IL_000e: stloc.0
IL_000f: ret

As you can see in the IL_0003 instruction, the value is getting boxed. Hence this style of conversion is least efficient of all others. So its better to avoid this style.

Now lets proceed to do some performance analysis on all three styles, although from the theoretical aspect from above lines we know their performance costs but still lets prove it with an example to convince fully. Hence i have used the below code:

static Stopwatch sp = new Stopwatch();
static int counter = 10000000;
static int valueToConvert = 70;

public static void Main()
{
    GC.WaitForPendingFinalizers();
    GC.Collect();
    for (int j = 0; j < 3; j++)
    {
        Console.WriteLine("Casting style...");
        StartTimer();
        for (int i = 0; i < counter; i++)
        {
            Char c = (char)valueToConvert;
        }

        StopTimer();
        PrintElapsedTime();

        Console.WriteLine("Using Convert helper method..");
        StartTimer();
        for (int i = 0; i < counter; i++)
        {
            Char c = Convert.ToChar(valueToConvert);
        }
        StopTimer();
        PrintElapsedTime();

        Console.WriteLine("Via IConvertible interface...");
        StartTimer();
        for (int i = 0; i < counter; i++)
        {
            Char c = ((IConvertible)valueToConvert).ToChar(null);
        }
        StopTimer();
        PrintElapsedTime();

        Console.WriteLine("\n\n");
    }
    Console.ReadLine();
}

As you can see from the above test code, i am taking 3 samples of the performance results just to make sure output is quite accurate. Hence from this, i got the below result:

Casting style…
Time elapsed in ms = 22
Using Convert helper method..
Time elapsed in ms = 60
Via IConvertible interface…
Time elapsed in ms = 233

Casting style…
Time elapsed in ms = 255
Using Convert helper method..
Time elapsed in ms = 294
Via IConvertible interface…
Time elapsed in ms = 417

Casting style…
Time elapsed in ms = 439
Using Convert helper method..
Time elapsed in ms = 478
Via IConvertible interface…
Time elapsed in ms = 598

From the above results it is very much clear that the performance issues is much better with the direct casting and acceptable with Convert helper methods in this case.

Hope it helps.

Thanks for reading. Your votes/comments are much appreciated.

Happy Coding,

Thanks, Zen 🙂

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s