Beim Vergleichen von Dictionary-Lookup und multiplen is-Operatoren in .NET 4.7 treten unerwartete Leistungsergebnisse auf

8

Ich habe das Problem, wo ich basierend auf einem Objekttyp dynamischen Versand durchführen muss. Die Typen, auf deren Grundlage ich versenden muss, sind zur Kompilierzeit bekannt - in meinem Beispiel sind sie 17 .

Meine anfängliche Schätzung war, ein Dictionary<Type, Action<Object>> für das Versenden zu verwenden und obj.GetType() zu verwenden, um die entsprechende Aktion herauszufinden. Aber dann entschied ich mich, BenchmarkDotNet zu verwenden, um zu sehen, ob ich besser und genauer tun kann, wie teuer die Versandsuche sein würde. Bellow ist der Code, den ich für den Benchmark verwendet habe.

%Vor%

In meinem Beispiel habe ich den Test mit einem Boxed Guid durchgeführt, was der schlimmste Fall in der handgefertigten Switch-Funktion ist. Die Ergebnisse waren überraschend, um es gelinde auszudrücken:

%Vor%

Die Switch-Funktion ist im schlimmsten Fall 2-mal schneller. Wenn ich die IFS so umordne, dass die häufigsten Typen zuerst sind, dann erwarte ich im Durchschnitt, dass sie 3-5 mal schneller läuft.

Meine Frage ist, wie kommt es, dass 18 Checks so viel schneller sind als ein einzelnes Wörterbuch? Fehle ich etwas Offensichtliches?

BEARBEITEN:

Der ursprüngliche Test war der x86-Modus (Prefer 32bit) auf dem x64-Computer. Ich habe die Tests auch in 64 Release Builds durchgeführt:

%Vor%     
Ivan Zlatanov 28.12.2017, 12:39
quelle

1 Antwort

5

Ich bin keineswegs ein IL-Performance-Guru, aber wenn Sie die IL dekompilieren und besonders betrachten, ergibt das Sinn.

Der is Operator ist nur 4 Opcodes (ldarg, isinst, ldnull, cgt), und jeder switch Teil nur 7 insgesamt mit dem hinzugefügten goto. Der Aktionsteil von Switch zum Aufruf von Empty() ist dann weitere 6, geben 17 * 7 + 6 = 125 max.

Im Gegensatz dazu ist Dictionary.TryGetValue möglicherweise nur ein Methodenaufruf, aber innerhalb dieses Prozesses werden viele Werte für Hashing, Schleifen und Vergleichen verarbeitet:

Ссылка

%Vor%

Ссылка

%Vor%

Die for-Schleife in FindEntry allein ist 31 Opcodes für jede Schleife, was maximal 527 Opcodes nur für diesen Teil ergibt.

Dies ist eine sehr einfache Analyse, aber es ist leicht zu erkennen, dass der Switch leistungsfähiger sein sollte. Wie es häufig der Fall ist, müssen Sie Leistung gegenüber Lesbarkeit und Wartbarkeit berücksichtigen. Wenn Sie einen Dictionary-Lookup verwenden, der Ihnen einen besseren Code bietet, ist es selten, dass der Leistungsverlust (15ns) diesen Nutzen überwiegt.

    
Rhumborl 28.12.2017, 13:27
quelle

Tags und Links