Performance of TypeCasting
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.
Solution 1:
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:
@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
}