Производительность TypeCasting
существует ли какая-либо заметная разница в производительности между
((TypeA) obj).method1();
((TypeA) obj).method2();
((TypeA) obj).method3();
и
var A = (TypeA) obj;
A.method1();
A.method2();
A.method3();
когда используется много раз?
Я часто вижу что-то вроде
if (((TextBox)sender).Text.Contains('.') || ((TextBox)sender).Text.Contains(','))
и задаться вопросом, является ли это пустой тратой производительности.
Ответы
Ответ 1
Это может быть измеримо, если это делается миллиарды раз с очень небольшой работой. Я не знаю, будет ли CLR эффективно кэшировать тот факт, что отбор работал, поэтому ему не нужно делать это снова - если это не так сейчас, оно может сделать это в более поздней версии. Он может сделать это в 64-битной JIT, но не в 32-битной версии, или наоборот - вы получите эту идею. Я сомневаюсь, что это существенно повлияет на нормальный код.
Лично мне нравится читаемость второй формы, хотя и более важная.
Ответ 2
@dkson: Я тестировал оба метода. Вот что я нашел на своем компьютере:
Они примерно одинаковы по производительности. Фактически, второй метод, который я обнаружил, немного медленнее. Причина (я считаю) - это стоимость дополнительной переменной и начального броска. Конечно, если вы используете достаточное количество бросков, вы можете вернуть эту стоимость. Похоже, вы ломаетесь даже с точки зрения производительности только после сохранения 20-30 бросков.
Ниже приведены результаты двух последних тестовых прогонов:
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
Я также проверил разницу между castclass
и isinst
. Основываясь на том, что я читал:
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
Я думал, что isinst будет быстрее, чем castclass, даже если не было никаких исключений. Однако, после создания моих собственных тестов, я обнаружил, что он немного медленнее, чем castclass. Очень интересно. Вот мои результаты:
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
Итак, мистер Скит, я стою исправлен.
Окружающая среда:
Windows Vista
Максимальная частота ядра 3,2 ГГц
.NET Framework v2.0.50727
Вот полный источник тестов, которые я создал и запускал:
(использует доступную инфраструктуру для работы с микропланшетами Jon Skeets здесь)
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();
}
}
}
И полученный IL для методов isinst
и castclass
:
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
}