Heute ist eine weitere Tierfabrik in die Bewertung eingeflogen:public static class AnimalsFactory
{
public static Animal CreateAnimalByTernaryOperator(bool isCat)
{
return isCat ? (Animal)new Cat() : new Dog();
}
}
Wieder einmal war ich verärgert, dass C # Sie zwingt, das Cat-Objekt auf Animal zu werfen. Aber lassen Sie die Kaste besser sein, denn durch die if-else-Anweisung ist der Code noch länger:public static class AnimalsFactory
{
public static Animal CreateAnimalByIfElseOperator(bool isCat)
{
if (isCat)
return new Cat();
return new Dog();
}
}
Lassen Sie uns eine Minute von der Überprüfung abschweifen und versuchen, es herauszufinden:- Wird der IL-Code in diesen Beispielen unterschiedlich sein?
- Wird eines der Beispiele von der Leistung profitieren?
Die Antwort auf die erste Frage lautet "Ja". Der IL-Code ist anders. Ich werde im Folgenden zeigen, was.Kommen wir zum Thema Leistung. Laden Sie das Nuget-Paket für BenchmarkDotNet-Benchmarks herunter und schreiben Sie einen Test:public class AnimalFactoryPerformanceTests
{
[ParamsAllValues]
public bool IsCat { get; set; }
[Benchmark]
public void CreateAnimalByTernaryOperator() =>
AnimalsFactory.CreateAnimalByTernaryOperator(IsCat);
[Benchmark]
public void CreateAnimalByIfElseOperator() =>
AnimalsFactory.CreateAnimalByIfElseOperator(IsCat);
}
Benchmark-Ergebnisse:| | IsCat | |
|------------------------------ |------ |---------:|
| CreateAnimalByTernaryOperator | False | 1.357 ns |
| CreateAnimalByTernaryOperator | True | 1.655 ns |
|------------------------------ |------ |---------:|
| CreateAnimalByIfElseOperator | False | 1.636 ns |
| CreateAnimalByIfElseOperator | True | 1.360 ns |
Es ist überraschend, dass "für einen Hund" ternär schneller funktioniert und "für eine Katze" - eine Wenn-Sonst-Aussage.Wir betrachten den IL-Code der Methode mit dem ternären Operator: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
}
Beim Erstellen des Dog- Objekts werden die Befehle IL_0000 - IL_0008 nacheinander ausgeführt , während beim Erstellen des Cat- Objekts ein bedingter Sprung erfolgt ( IL_0001: brtrue.s IL_0009 ).Wie Sie vielleicht erraten haben, wird für die if-else-Anweisung IL-Code generiert, der keine bedingten Sprünge zum Erstellen eines Cat- Objekts erfordert . Während das Dog- Objekt über einen bedingten Zweig erstellt wird: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
}
Fügen Sie die Parrot-Erstellung und eine neue Methode mit der switch-Anweisung zur Factory hinzu :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();
}
}
}
Welche Methode wird schneller sein?Benchmark-Ergebnisse| | 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 |
IL-CodeCreateAnimalByTernaryOperator(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
}
Schlussfolgerung 1: Bei ternären und if-else-Anweisungen hängt die Laufzeit direkt von der Anzahl der bedingten Sprünge ab, die im Ausführungsthread übergeben wurden.Schlussfolgerung 2: Die C # -Switch-Anweisung wird im IL-Code in eine Switch-Anweisung konvertiert und funktioniert im Durchschnitt etwas länger als normale Verzweigungsanweisungen.Schlussfolgerung 3: Alles oben Genannte ist nur für .NET Framework v.4.8 relevant. Nachdem wir dieselben Tests auf .NetCore durchgeführt haben, haben wir völlig unterschiedliche Ergebnisse erhalten, die noch irgendwie interpretiert werden müssen.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 |
Prozessor: Intel® Core (TM) i7-7700K-Quellen