Performance of TypeCasting

后端 未结 2 2020
长发绾君心
长发绾君心 2020-12-02 16:55

is there any measurably performance difference between

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

and

相关标签:
2条回答
  • 2020-12-02 17:10

    @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 
    }
    
    0 讨论(0)
  • 2020-12-02 17:18

    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.

    0 讨论(0)
提交回复
热议问题