views:

254

answers:

2

Hi there,

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.

+10  A: 

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.

Jon Skeet
+1 also I think typecasting should be done using the "as" operator and checked for null later, unless you *want* an exception to be thrown.
Stan R.
Agreed, although often I see "as" being used when a cast *should* be used. Very often "the wrong type" == "bug", in which case the simple cast is the right way to go.
Jon Skeet
On the topic of performance, the "as" operator causes the csc to emit "isinst" opcode (vs "Castclass" opcode for bog style) which is up to 8x faster when measured in millions of casts.
Robert Venables
@novatrust: I've benchmarked this various times and *never* seen it being significantly faster. What version of .NET were you testing on? Such a claim requires evidence, IMO...
Jon Skeet
(Of course the isinst will be much *much* faster if the cast fails, but then we're talking about a very different situation, where the *result* is radically different between the two. When the cast succeeds, and when it's a straight cast rather than a user conversion, the performance should be about the same.)
Jon Skeet
@Jon Skeet, see my reply below.
Robert Venables
+5  A: 

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: http://www.yoda.arachsys.com/csharp/benchmark.html)

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 
}
Robert Venables