2009-07-22 5 views
48

est-il une différence de performance entre mesurablementPerformance de transtypage

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

et

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

quand il est utilisé beaucoup de temps?

Je vois souvent quelque chose comme

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

et se demander si cela est une perte de performance.

+7

La seule façon de répondre "existe-t-il une différence mesurable" est de le mesurer et de le voir. Rien ne vous empêche de le faire ... –

Répondre

48

Cela peut être mesurable si cela se fait des milliards de fois avec très peu d'autres travaux. Je ne sais pas si le CLR mettra efficacement en cache le fait que le cast a fonctionné, donc il n'a pas besoin de le faire à nouveau - s'il ne le fait pas maintenant, il pourrait le faire dans une version ultérieure. Il pourrait le faire dans le JIT 64 bits mais pas dans la version 32 bits, ou vice versa - vous avez l'idée. Je doute que cela ferait une différence significative dans le code normal cependant.

Personnellement, j'aime plus la lisibilité de la seconde forme, et c'est de loin plus important.

+2

+1 aussi je pense que le typage devrait être fait en utilisant l'opérateur "as" et vérifié pour null plus tard, à moins que vous * vouliez * qu'une exception soit levée. –

+5

D'accord, bien que souvent je vois "comme" étant utilisé quand un casting * devrait * être utilisé. Très souvent "le mauvais type" == "bug", auquel cas la distribution simple est la bonne façon de procéder. –

+0

En ce qui concerne les performances, l'opérateur "as" fait en sorte que csc émette l'opcode "isinst" (contre l'opcode "Castclass" pour le style de tourbière) qui est jusqu'à 8 fois plus rapide en millions de conversions. –

35

@dkson: J'ai testé les deux méthodes. Voici ce que j'ai trouvé sur mon ordinateur:

Ils sont sur le mêmes performance. En fait, la deuxième méthode que j'ai trouvée est légèrement plus lente. La raison (je crois) est le coût de la variable supplémentaire et de la distribution initiale. Bien sûr, si vous utilisez suffisamment de moulages, vous pouvez récupérer ce coût de performance. Il semble que vous ayez atteint le seuil de performance après avoir sauvé 20-30 lancers.

Voici les résultats des deux essais les plus récents:

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 

J'ai aussi testé la différence entre castclass et isinst. Sur la base de ce que j'avais lu:

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

Je pensais que isinst serait plus rapide que castclass même quand il n'y avait pas d'exception. Cependant, après avoir créé mes propres benchmarks, j'ai trouvé que isinst était légèrement plus faible que que castclass. Très intéressant. Voici mes résultats:

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 

Ainsi, M. Skeet, je suis corrigé.

Environnement:

Windows Vista
Maximum de base Vitesse 3.2Ghz
v2.0 .NET Framework.50727

Voici la source complète aux points de référence que je crée et dirige: (fait usage du cadre Jon Skeets Microbenchmarking disponible 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(); 
     } 
    } 
} 

Et le résultat IL pour isinst et castclass méthodes:

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 
}