Ich habe ein Programm, das eine schnelle Leistung erfordert. Innerhalb einer seiner inneren Schleifen muss ich den Typ eines Objekts testen, um festzustellen, ob es von einer bestimmten Schnittstelle erbt.
Eine Möglichkeit hierfür wäre die integrierte Typprüfungsfunktion der CLR. Die eleganteste Methode ist wahrscheinlich das Schlüsselwort 'is':
if (obj is ISpecialType)
Ein anderer Ansatz wäre, der Basisklasse meine eigene virtuelle GetType () -Funktion zu geben, die einen vordefinierten Aufzählungswert zurückgibt (in meinem Fall brauche ich eigentlich nur einen Bool). Diese Methode wäre schnell, aber weniger elegant.
Ich habe gehört, dass es eine IL-Anweisung speziell für das Schlüsselwort 'is' gibt, aber das bedeutet nicht, dass sie schnell ausgeführt wird, wenn sie in eine native Assembly übersetzt wird. Kann jemand einen Einblick in die Leistung von 'is' im Vergleich zur anderen Methode geben?
UPDATE: Danke für all die informierten Antworten! Es scheint, dass einige hilfreiche Punkte in den Antworten verteilt sind: Andrews Punkt über das automatische Durchführen einer Besetzung ist wichtig, aber die von Binary Worrier und Ian gesammelten Leistungsdaten sind auch äußerst nützlich. Es wäre großartig, wenn eine der Antworten so bearbeitet würde, dass sie alle diese Informationen enthält.
quelle
Antworten:
Die Verwendung
is
kann die Leistung beeinträchtigen, wenn Sie nach Überprüfung des Typs auf diesen Typ umwandeln.is
Wirkt das Objekt tatsächlich in den Typ, den Sie überprüfen, sodass jede nachfolgende Umwandlung redundant ist.Wenn Sie trotzdem besetzen wollen, ist hier ein besserer Ansatz:
quelle
as
führt grundsätzlich die gleiche Operation aus wieis
(nämlich die Typprüfung). Der einzige Unterschied ist , dass es dann wiedernull
stattfalse
.Ich bin bei Ian , du willst das wahrscheinlich nicht.
Nur damit Sie wissen, gibt es kaum einen Unterschied zwischen den beiden, über 10.000.000 Iterationen
Ich persönlich würde dieses Problem nicht auf diese Weise beheben, aber wenn ich gezwungen wäre, eine Methode auszuwählen, wäre dies die integrierte IS-Prüfung. Der Leistungsunterschied ist es nicht wert, den Codierungsaufwand zu berücksichtigen.
Meine Basis- und abgeleiteten Klassen
JubJub: Wie gewünscht mehr Infos zu den Tests.
Ich habe beide Tests über eine Konsolen-App (ein Debug-Build) ausgeführt. Jeder Test sieht wie folgt aus
Wenn ich im Release laufe, bekomme ich wie Ian einen Unterschied von 60 - 70 ms.
Weiteres Update - 25. Oktober 2012
Nach ein paar Jahren ist mir etwas daran aufgefallen. Der Compiler kann dies
bool b = a is MyClassB
in der Version weglassen , da b nirgendwo verwendet wird.Dieser Code. . .
. . . Zeigt konsistent an, dass der
is
Check bei ca. 57 Millisekunden und der Enum-Vergleich bei 29 Millisekunden eingeht.NB Ich würde den
is
Scheck immer noch vorziehen , der Unterschied ist zu gering, um sich darum zu kümmernquelle
is
Bediener verursacht, und dass das übermäßige Hören von Design und Codierung um denis
Bediener ein Vermögen kostet Codequalität und wird letztendlich auch in Bezug auf die Leistung selbstzerstörerisch sein. In diesem Fall stehe ich zu meiner Aussage. Der ‚ist‘ Operator wird nie sein werde das Problem mit der Laufzeitleistung.Ok, also habe ich mich mit jemandem darüber unterhalten und beschlossen, dies mehr zu testen. Soweit ich das beurteilen kann, sind die Leistung von
as
undis
beide sehr gut, verglichen mit dem Testen Ihres eigenen Mitglieds oder Ihrer Funktion zum Speichern von Typinformationen.Ich habe verwendet
Stopwatch
, was ich gerade gelernt habe, ist möglicherweise nicht der zuverlässigste Ansatz, also habe ich es auch versuchtUtcNow
. Später habe ich auch den Prozessorzeitansatz ausprobiert, der demUtcNow
Einschließen unvorhersehbarer Erstellungszeiten ähnelt . Ich habe auch versucht, die Basisklasse ohne Virtuals nicht abstrakt zu machen, aber es schien keinen signifikanten Effekt zu haben.Ich habe dies auf einem Quad Q6600 mit 16 GB RAM ausgeführt. Selbst bei 50-mil-Iterationen schwanken die Zahlen immer noch um +/- 50 Millisekunden, sodass ich nicht zu viel in die kleinen Unterschiede hineinlesen würde.
Es war interessant zu sehen, dass x64 schneller erstellt, aber langsamer ausgeführt wird als x86
x64-Freigabemodus:
Stoppuhr:
As: 561 ms
Is: 597
ms Basiseigenschaft : 539 ms Basisfeld : 555
ms
Basis-RO-Feld: 552 ms
Virtual GetEnumType () -Test: 556 ms
Virtual IsB () -Test: 588 ms
Erstellungszeit: 10416 ms
UtcNow:
As: 499 ms
Is: 532 ms
Base-Eigenschaft: 479 ms
Base-Feld: 502 ms
Base-RO-Feld: 491 ms
Virtual GetEnumType (): 502 ms
Virtual bool IsB (): 522 ms
Erstellungszeit: 285 ms (Diese Zahl scheint mit UtcNow unzuverlässig zu sein. Ich erhalte auch 109 ms und 806 ms.)
x86-Freigabemodus:
Stoppuhr:
As: 391 ms
Is: 423
ms Basiseigenschaft : 369 ms Basisfeld : 321
ms
Basis-RO-Feld: 339 ms
Virtual GetEnumType () -Test: 361 ms
Virtual IsB () -Test: 365 ms
Erstellungszeit: 14106 ms
UtcNow:
As: 348ms
Is: 375ms
Base-Eigenschaft: 329ms
Base-Feld: 286 ms
Base-RO-Feld: 309 ms
Virtual GetEnumType (): 321 ms
Virtual bool IsB (): 332 ms
Erstellungszeit: 544 ms (Diese Zahl scheint mit UtcNow unzuverlässig zu sein.)
Hier ist der größte Teil des Codes:
quelle
Andrew ist richtig. Bei der Codeanalyse wird dies von Visual Studio als unnötige Besetzung gemeldet.
Eine Idee (ohne zu wissen, was Sie tun, ist ein Schuss in die Dunkelheit), aber mir wurde immer geraten, solche Überprüfungen zu vermeiden und stattdessen eine andere Klasse zu haben. Lassen Sie die Klasse also wissen, wie sie sich selbst verarbeitet, anstatt einige Überprüfungen durchzuführen und je nach Typ unterschiedliche Aktionen auszuführen ...
zB Obj kann ISpecialType oder IType sein;
Für beide ist eine DoStuff () -Methode definiert. Für IType kann es einfach zurückgeben oder benutzerdefinierte Aufgaben ausführen, während ISpecialType andere Aufgaben ausführen kann.
Dadurch wird jegliches Casting vollständig entfernt, der Code wird sauberer und einfacher zu warten, und die Klasse weiß, wie sie ihre eigenen Aufgaben erledigt.
quelle
Ich habe einen Leistungsvergleich über zwei Möglichkeiten des Typvergleichs durchgeführt
Das Ergebnis ist: Die Verwendung von "is" ist ungefähr 10x schneller !!!
Ausgabe:
Zeit für den Typvergleich: 00: 00: 00.456
Zeit für den Is-Vergleich: 00: 00: 00.042
Mein Code:
quelle
Punkt Andrew Hare machte über Leistungsverluste, wenn Sie eine
is
Prüfung durchführen und dann die Besetzung gültig war, aber in C # 7.0 können wir die Übereinstimmung der Hexenmuster überprüfen, um später eine zusätzliche Besetzung zu vermeiden:Wenn Sie zwischen mehreren Typen prüfen müssen, können Sie mit C # 7.0-Mustervergleichskonstrukten jetzt folgende
switch
Typen ausführen:Weitere Informationen zum Mustervergleich in C # finden Sie in der Dokumentation hier .
quelle
OneOf<T...>
aber sie weisen erhebliche Mängel auf). .Für den Fall, dass sich jemand wundert, habe ich Tests in Unity Engine 2017.1 mit der Skript-Laufzeitversion .NET4.6 (Experimantal) auf einem Notebook mit i5-4200U-CPU durchgeführt. Ergebnisse:
Average Relative To Local Call LocalCall 117.33 1.00 is 241.67 2.06 Enum 139.33 1.19 VCall 294.33 2.51 GetType 276.00 2.35
Vollständiger Artikel: http://www.ennoble-studios.com/tuts/unity-c-performance-comparison-is-vs-enum-vs-virtual-call.html
quelle
Mir wurde immer geraten, solche Überprüfungen zu vermeiden und stattdessen eine andere Klasse zu haben. Lassen Sie die Klasse also wissen, wie sie sich selbst verarbeitet, anstatt einige Überprüfungen durchzuführen und je nach Typ unterschiedliche Aktionen auszuführen ...
zB Obj kann ISpecialType oder IType sein;
Für beide ist eine DoStuff () -Methode definiert. Für IType kann es einfach zurückgeben oder benutzerdefinierte Aufgaben ausführen, während ISpecialType andere Aufgaben ausführen kann.
Dadurch wird jegliches Casting vollständig entfernt, der Code wird sauberer und einfacher zu warten, und die Klasse weiß, wie sie ihre eigenen Aufgaben erledigt.
quelle