Performance of TypeCasting

C#.NetPerformanceTypes

C# Problem Overview


is there any measurably performance difference between

((TypeA) obj).method1();
((TypeA) obj).method2();
((TypeA) obj).method3();

and

var A = (TypeA) obj;
A.method1();
A.method2();
A.method3();

when used alot of times?

I often see something like

if (((TextBox)sender).Text.Contains('.') || ((TextBox)sender).Text.Contains(','))

and wonder if this is a waste of performance.

C# Solutions


Solution 1 - C#

It may be measurable if it's being done billions of times with very little other work. I don't know whether the CLR will effectively cache the fact that the cast worked, so it doesn't need to do it again - if it doesn't do so now, it might do so in a later release. It might do so in the 64 bit JIT but not the 32 bit version, or vice versa - you get the idea. I doubt that it would make a significant difference in normal code though.

Personally I like the readability of the second form more though, and that's more important by far.

Solution 2 - C#

@dkson: I tested both methods. Here’s what I found on my computer:

They are about the same in performance. In fact, the second method I found to be slightly slower. The reason (I believe) is the cost of the extra variable and initial cast. Of course, if you use enough casts you may get that performance cost back. It looks like you break even in terms of performance only after saving 20-30 casts.

Here are the results from the two most recent test runs:

TestMuliCast\_3x:      00:00:00.5970000   
TestSingleCast\_3x:    00:00:00.6020000   
TestMuliCast\_30x:     00:00:06.0930000   
TestSingleCast\_30x:   00:00:06.0480000   

TestMuliCast\_3x:      00:00:00.6120000   
TestSingleCast\_3x:    00:00:00.6250000   
TestMuliCast\_30x:     00:00:06.5490000   
TestSingleCast\_30x:   00:00:06.4440000   

I also tested the difference between castclass and isinst. Based on what I had read:

http://m3mia.blogspot.com/2007/11/comparing-isinst-to-castclass.html
http://www.codeproject.com/KB/cs/csharpcasts.aspx
http://discuss.joelonsoftware.com/default.asp?dotnet.12.635066.13

I thought isinst would be faster than castclass even when there were no exceptions. However, after creating my own benchmarks, I found isinst to be slightly slower than castclass. Very Interesting. Here are my results:

TestEmptyLoop:        00:00:00.0870000   
TestDCast\_castclass:  00:00:00.2640000   
TestDCast\_isinst:     00:00:00.3780000   

TestEmptyLoop:        00:00:00.0870000   
TestDCast\_castclass:  00:00:00.2600000   
TestDCast\_isinst:     00:00:00.3750000   

So, Mr. Skeet, I stand corrected.

Environment:

> Windows Vista
Maximum Core Speed 3.2Ghz
.NET Framework v2.0.50727

Here is the full source to the benchmarks that I created and ran: (makes use of Jon Skeets Microbenchmarking framework available here)

using System;   
using System.Collections;

public class CastingBenchmark   
{
    static Int64 Iterations=100000000;
    static Int64 TestWork = 0;

    public static void Init(string[] args)
    {
        if (args.Length>0)
            Iterations = Int64.Parse(args[0]);
    }

    public static void Reset()
    {
        TestWork = 0;
    }

    internal class BaseType { public void TestBaseMethod() { TestWork++; } }

    internal class DerivedType : BaseType { 
        public void TestDerivedMethod() { TestWork++; }
        public void TestDerivedMethod2() { TestWork++; }
        public void TestDerivedMethod3() { TestWork++; } 
}

[Benchmark]
public static void TestMuliCast_3x()
{
    BaseType TestBaseType = new DerivedType();

    for (int x = 0; x < Iterations; x++)
    {
        ((DerivedType)TestBaseType).TestDerivedMethod();
        ((DerivedType)TestBaseType).TestDerivedMethod2();
        ((DerivedType)TestBaseType).TestDerivedMethod3();
    }
}

[Benchmark]
public static void TestSingleCast_3x()
{
    BaseType TestBaseType = new DerivedType();

    for (int x = 0; x < Iterations; x++)
    {
        DerivedType TestDerivedType = (DerivedType)TestBaseType;
        TestDerivedType.TestDerivedMethod();
        TestDerivedType.TestDerivedMethod2();
        TestDerivedType.TestDerivedMethod3();
    }
}

[Benchmark]
public static void TestMuliCast_30x()
{
    BaseType TestBaseType = new DerivedType();

    for (int x = 0; x < Iterations; x++)
    {
        //Simulate 3 x 10 method calls while casting
        for (int y = 0; y < 10; y++) {

            ((DerivedType)TestBaseType).TestDerivedMethod();
            ((DerivedType)TestBaseType).TestDerivedMethod2();
            ((DerivedType)TestBaseType).TestDerivedMethod3();
        }
    }
}

[Benchmark]
public static void TestSingleCast_30x()
{
    BaseType TestBaseType = new DerivedType();

    for (int x = 0; x < Iterations; x++)
    {
        DerivedType TestDerivedType = (DerivedType)TestBaseType;
        
        //Simulate 3 x 10 method calls on already-cast object
        for (int y = 0; y < 10; y++)
        {
            TestDerivedType.TestDerivedMethod();
            TestDerivedType.TestDerivedMethod2();
            TestDerivedType.TestDerivedMethod3();
        }
    }
}

    [Benchmark]
    public static void TestEmptyLoop()
    {
        for (int x = 0; x < Iterations; x++)
        {
        }
    }

    [Benchmark]
    public static void TestDCast_castclass()
    {
        BaseType TestDerivedType = new DerivedType();

        for (int x = 0; x < Iterations; x++)
        {
            ((DerivedType)TestDerivedType).TestDerivedMethod();
        }    
    }

    [Benchmark]
    public static void TestDCast_isinst()
    {
        BaseType TestDerivedType = new DerivedType();

        for (int x = 0; x < Iterations; x++)
        {
            (TestDerivedType as DerivedType).TestDerivedMethod();
        }
    }
}

And the resulting IL for isinst and castclass methods:

method public hidebysig static void TestDCast_isinst() cil managed
{
    .custom instance void BenchmarkAttribute::.ctor()
    .maxstack 2
    .locals init (
        [0] class CastingBenchmark/BaseType TestDerivedType,
        [1] int32 x)
    L_0000: newobj instance void CastingBenchmark/DerivedType::.ctor()
    L_0005: stloc.0 
    L_0006: ldc.i4.0 
    L_0007: stloc.1 
    L_0008: br.s L_0019
    L_000a: ldloc.0 
    L_000b: isinst CastingBenchmark/DerivedType
    L_0010: callvirt instance void CastingBenchmark/DerivedType::TestDerivedMethod()
    L_0015: ldloc.1 
    L_0016: ldc.i4.1 
    L_0017: add 
    L_0018: stloc.1 
    L_0019: ldloc.1 
    L_001a: conv.i8 
    L_001b: ldsfld int64 CastingBenchmark::Iterations
    L_0020: blt.s L_000a
    L_0022: ret 
}

.method public hidebysig static void TestDCast_castclass() cil managed
{
    .custom instance void BenchmarkAttribute::.ctor()
    .maxstack 2
    .locals init (
        [0] class CastingBenchmark/BaseType TestDerivedType,
        [1] int32 x)
    L_0000: newobj instance void CastingBenchmark/DerivedType::.ctor()
    L_0005: stloc.0 
    L_0006: ldc.i4.0 
    L_0007: stloc.1 
    L_0008: br.s L_0019
    L_000a: ldloc.0 
    L_000b: castclass CastingBenchmark/DerivedType
    L_0010: callvirt instance void CastingBenchmark/DerivedType::TestDerivedMethod()
    L_0015: ldloc.1 
    L_0016: ldc.i4.1 
    L_0017: add 
    L_0018: stloc.1 
    L_0019: ldloc.1 
    L_001a: conv.i8 
    L_001b: ldsfld int64 CastingBenchmark::Iterations
    L_0020: blt.s L_000a
    L_0022: ret 
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionslothView Question on Stackoverflow
Solution 1 - C#Jon SkeetView Answer on Stackoverflow
Solution 2 - C#Robert VenablesView Answer on Stackoverflow