Aujourd'hui, une autre animalerie s'est lancée dans la revue:public static class AnimalsFactory
{
public static Animal CreateAnimalByTernaryOperator(bool isCat)
{
return isCat ? (Animal)new Cat() : new Dog();
}
}
Encore une fois, j'étais contrarié que C # m'oblige à lancer l'objet Chat sur Animal. Mais que la caste soit meilleure, car grâce à l'instruction if-else, le code est encore plus long:public static class AnimalsFactory
{
public static Animal CreateAnimalByIfElseOperator(bool isCat)
{
if (isCat)
return new Cat();
return new Dog();
}
}
Écartons-nous une minute de l'examen et essayons de comprendre:- Le code IL sera-t-il différent dans ces exemples?
- l'un des exemples bénéficiera-t-il des performances?
La réponse à la première question est oui, le code IL est différent, je vais montrer ci-dessous quoi.Passons à la question de la performance. Téléchargez le package nuget pour les benchmarks BenchmarkDotNet et écrivez un test:public class AnimalFactoryPerformanceTests
{
[ParamsAllValues]
public bool IsCat { get; set; }
[Benchmark]
public void CreateAnimalByTernaryOperator() =>
AnimalsFactory.CreateAnimalByTernaryOperator(IsCat);
[Benchmark]
public void CreateAnimalByIfElseOperator() =>
AnimalsFactory.CreateAnimalByIfElseOperator(IsCat);
}
Résultats de référence:| | IsCat | |
|------------------------------ |------ |---------:|
| CreateAnimalByTernaryOperator | False | 1.357 ns |
| CreateAnimalByTernaryOperator | True | 1.655 ns |
|------------------------------ |------ |---------:|
| CreateAnimalByIfElseOperator | False | 1.636 ns |
| CreateAnimalByIfElseOperator | True | 1.360 ns |
Il est surprenant que "pour un chien" ternaire fonctionne plus rapidement, et "pour un chat" - une déclaration si-sinon.Nous regardons le code IL de la méthode avec l'opérateur ternaire:CreateAnimalByTernaryOperator(bool isCat)
{
IL_0000: ldarg.0
IL_0001: brtrue.s IL_0009
IL_0003: newobj instance void AnimalPerformance.Dog::.ctor()
IL_0008: ret
IL_0009: newobj instance void AnimalPerformance.Cat::.ctor()
IL_000e: ret
}
Lors de la création de l'objet Dog , les commandes IL_0000 - IL_0008 sont exécutées séquentiellement , tandis que lors de la création de l'objet Cat , un saut conditionnel se produit ( IL_0001: brtrue.s IL_0009 ).Comme vous pouvez le deviner, pour l'instruction if-else, un code IL est généré qui ne nécessite pas de sauts conditionnels pour créer un objet Cat . Alors que l'objet Dog est créé via une branche conditionnelle:CreateAnimalByIfElseOperator(bool isCat)
{
IL_0000: ldarg.0
IL_0001: brfalse.s IL_0009
IL_0003: newobj instance void AnimalPerformance.Cat::.ctor()
IL_0008: ret
IL_0009: newobj instance void AnimalPerformance.Dog::.ctor()
IL_000e: ret
}
Ajoutez la création Parrot et une nouvelle méthode avec l' instruction switch à l'usine :public static class AnimalFactory
{
public static Animal CreateAnimalByTernaryOperator(AnimalType animalType)
{
return animalType == AnimalType.Cat
? new Cat()
: animalType == AnimalType.Dog
? (Animal)new Dog()
: new Parrot();
}
public static Animal CreateAnimalByIfElseOperator(AnimalType animalType)
{
if (animalType == AnimalType.Cat)
return new Cat();
if (animalType == AnimalType.Dog)
return new Dog();
return new Parrot();
}
public static Animal CreateAnimalBySwitchOperator(AnimalType animalType)
{
switch (animalType)
{
case AnimalType.Cat:
return new Cat();
case AnimalType.Dog:
return new Dog();
case AnimalType.Parrot:
return new Parrot();
default:
throw new InvalidOperationException();
}
}
}
Quelle méthode sera plus rapide?Résultats de référence| | AnimalType | |
|------------------------------ |----------- |---------:|
| CreateAnimalByTernaryOperator | Cat | 2.490 ns |
| CreateAnimalByTernaryOperator | Dog | 2.515 ns |
| CreateAnimalByTernaryOperator | Parrot | 2.333 ns |
|------------------------------ |----------- |---------:|
| CreateAnimalByIfElseOperator | Cat | 2.368 ns |
| CreateAnimalByIfElseOperator | Dog | 2.545 ns |
| CreateAnimalByIfElseOperator | Parrot | 2.735 ns |
|------------------------------ |----------- |---------:|
| CreateAnimalBySwitchOperator | Cat | 2.747 ns |
| CreateAnimalBySwitchOperator | Dog | 2.730 ns |
| CreateAnimalBySwitchOperator | Parrot | 2.722 ns |
Code ILCreateAnimalByTernaryOperator(AnimalsFactory.AnimalType animalType)
{
IL_0000: ldarg.0
IL_0001: brfalse.s IL_0013
IL_0003: ldarg.0
IL_0004: ldc.i4.1
IL_0005: beq.s IL_000d
IL_0007: newobj instance void AnimalsFactory.Parrot::.ctor()
IL_000c: ret
IL_000d: newobj instance void AnimalsFactory.Dog::.ctor()
IL_0012: ret
IL_0013: newobj instance void AnimalsFactory.Cat::.ctor()
IL_0018: ret
}
CreateAnimalByIfElseOperator(AnimalsFactory.AnimalType animalType)
{
IL_0000: ldarg.0
IL_0001: brtrue.s IL_0009
IL_0003: newobj instance void AnimalsFactory.Cat::.ctor()
IL_0008: ret
IL_0009: ldarg.0
IL_000a: ldc.i4.1
IL_000b: bne.un.s IL_0013
IL_000d: newobj instance void AnimalsFactory.Dog::.ctor()
IL_0012: ret
IL_0013: newobj instance void AnimalsFactory.Parrot::.ctor()
IL_0018: ret
}
CreateOtherAnimalBySwitchOperator(AnimalsFactory.AnimalType animalType)
{
IL_0000: ldarg.0
IL_0001: switch (IL_0014, IL_001a, IL_0020)
IL_0012: br.s IL_0026
IL_0014: newobj instance void AnimalsFactory.Cat::.ctor()
IL_0019: ret
IL_001a: newobj instance void AnimalsFactory.Dog::.ctor()
IL_001f: ret
IL_0020: newobj instance void AnimalsFactory.Parrot::.ctor()
IL_0025: ret
IL_0026: newobj instance void System.InvalidOperationException::.ctor()
IL_002b: throw
}
Conclusion 1: pour les instructions ternaires et if-else, l'exécution dépend directement du nombre de sauts conditionnels qui se sont passés dans le thread d'exécution.Conclusion 2: l' instruction switch C # est convertie en code IL en instruction switch et, en moyenne, fonctionne un peu plus longtemps que les instructions de branchement standard.Conclusion 3: Tout ce qui précède n'est pertinent que pour le .NET Framework v.4.8. Après avoir exécuté les mêmes tests sur .NetCore, nous avons obtenu des résultats complètement différents qui doivent encore être interprétés d'une manière ou d'une autre.Benchmarks .NetCore 3.0| | AnimalType | |
|------------------------------ |----------- |---------:|
| CreateAnimalByTernaryOperator | Cat | 3.046 ns |
| CreateAnimalByTernaryOperator | Dog | 2.984 ns |
| CreateAnimalByTernaryOperator | Parrot | 3.019 ns |
|------------------------------ |----------- |---------:|
| CreateAnimalByIfElseOperator | Cat | 2.980 ns |
| CreateAnimalByIfElseOperator | Dog | 2.977 ns |
| CreateAnimalByIfElseOperator | Parrot | 3.103 ns |
|------------------------------ |----------- |---------:|
| CreateAnimalBySwitchOperator | Cat | 3.519 ns |
| CreateAnimalBySwitchOperator | Dog | 3.533 ns |
| CreateAnimalBySwitchOperator | Parrot | 3.312 ns |
Processeur: Intel® Core (TM) i7-7700KSources