2012-05-05 9 views
29

PMD sagt mirEffizienz: switch-Anweisungen über if-Anweisungen

Ein Schalter mit weniger als 3 Zweige ineffizient ist, verwenden Sie eine if-Anweisung statt.

Warum ist das so? Warum 3? Wie definieren sie Effizienz?

+2

Was ist PMD? ? ? – jmort253

+3

PMD scannt Java-Quellcode und sucht nach möglichen Problemen wie möglichen Fehlern, totem Code, suboptimalem Code, überkomplizierten Ausdrücken und doppeltem Code. (Bewegen Sie die Maus über die Tags) – Chords

+9

Es sollte sich auch nach Grammatik durchsuchen lassen. "Weniger" sollte "weniger" sein. :) – yshavit

Antwort

36

Da eine switch-Anweisung mit zwei speziellen JVM-Anweisungen kompiliert wird, die lookupswitch und sind. Sie sind nützlich, wenn Sie mit vielen Fällen arbeiten, aber sie verursachen einen Overhead, wenn Sie nur wenige Zweige haben.

Eine if/else Anweisung ist stattdessen in typische jejne ... Ketten zusammengefasst, die schneller sind, aber viel mehr Vergleiche erfordern, wenn sie in einer langen Kette von Zweigen verwendet werden.

Sie können den Unterschied sehen, indem Sie sich den Byte-Code ansehen, in jedem Fall würde ich mich nicht um diese Probleme kümmern, wenn irgendetwas ein Problem werden könnte, dann wird JIT sich darum kümmern.

Praxisbeispiel:

switch (i) 
{ 
    case 1: return "Foo"; 
    case 2: return "Baz"; 
    case 3: return "Bar"; 
    default: return null; 
} 

in kompilierten:

L0 
LINENUMBER 21 L0 
ILOAD 1 
TABLESWITCH 
    1: L1 
    2: L2 
    3: L3 
    default: L4 
L1 
LINENUMBER 23 L1 
FRAME SAME 
LDC "Foo" 
ARETURN 
L2 
LINENUMBER 24 L2 
FRAME SAME 
LDC "Baz" 
ARETURN 
L3 
LINENUMBER 25 L3 
FRAME SAME 
LDC "Bar" 
ARETURN 
L4 
LINENUMBER 26 L4 
FRAME SAME 
ACONST_NULL 
ARETURN 

Während

if (i == 1) 
    return "Foo"; 
else if (i == 2) 
    return "Baz"; 
else if (i == 3) 
    return "Bar"; 
else 
    return null; 

in

zusammengestellt
L0 
LINENUMBER 21 L0 
ILOAD 1 
ICONST_1 
IF_ICMPNE L1 
L2 
LINENUMBER 22 L2 
LDC "Foo" 
ARETURN 
L1 
LINENUMBER 23 L1 
FRAME SAME 
ILOAD 1 
ICONST_2 
IF_ICMPNE L3 
L4 
LINENUMBER 24 L4 
LDC "Baz" 
ARETURN 
L3 
LINENUMBER 25 L3 
FRAME SAME 
ILOAD 1 
ICONST_3 
IF_ICMPNE L5 
L6 
LINENUMBER 26 L6 
LDC "Bar" 
ARETURN 
L5 
LINENUMBER 28 L5 
FRAME SAME 
ACONST_NULL 
ARETURN 
+0

Jack danke. Das ist eine wundervolle Antwort. Nebenbei, was hast du benutzt, um '.class' Dateien zu sehen? – JAM

+0

Es ist ein Plugin für Eclipse, wenn ich mich richtig erinnere sollte es dieses sein: http://andrei.gmxhome.de/bytecode/index.html – Jack

+1

@JAM: Ich glaube, Sie auch [javap] (http verwenden: // docs.oracle.com/javase/1.5.0/docs/tooldocs/windows/javap.html). – RanRag

7

Obwohl bei Verwendung eines Switches im Vergleich zur Verwendung einer if-Anweisung geringe Effizienzgewinne auftreten, wären diese Gewinne unter den meisten Umständen vernachlässigbar. Und jeder Quellcodeleser, der es wert ist, würde erkennen, dass micro-optimizations der Klarheit des Codes untergeordnet sind.

Sie sagen, dass eine if-Anweisung sowohl einfacher zu lesen ist als auch weniger Zeilen Code als eine switch-Anweisung benötigt, wenn der Schalter signifikant kurz ist.

Vom PMD website:

TooFewBranchesForASwitchStatement: Switch-Anweisungen werden indended verwendet werden, um komplexe Verzweigungsverhalten zu unterstützen. Die Verwendung eines Schalters für nur wenige Fälle ist nicht ratsam, da Schalter nicht so einfach zu verstehen sind wie Wenn-Dann-Anweisungen. In diesen Fällen verwenden Sie die Anweisung if-then, um die Lesbarkeit des Codes zu erhöhen.

+1

Die Bedeutung der Klarheit übersteigt bei weitem alle Mikrooptimierungen. Daran gibt es keinen Zweifel. – JAM

+5

Das schöne ist, dass sie in der Warnung "ineffizient" angeben, aber sie stellen fest, dass es nur in Bezug auf "Lesbarkeit" in der Dokumentation steht. Gute Kohärenz .. – Jack

1

Ich glaube, es mit der Art und Weise zu tun, dass ein Schalter, und einem if/else unten kompiliert.

Nehmen wir an, es dauert 5 Berechnungen, um eine switch-Anweisung zu verarbeiten. Sagen Sie, eine if-Anweisung benötigt zwei Berechnungen. Weniger als 3 Optionen in Ihrem Switch wären 4 Berechnungen in ifs gegenüber 5 in switches. Der Overhead bleibt jedoch in einem Switch konstant, so dass, wenn es 3 Auswahlmöglichkeiten hat, ifs 3 * 2 verarbeitet wird, während 5 noch für den Switch sind.

Die Gewinne bei der Betrachtung von Millionen von Berechnungen sind extrem vernachlässigbar. Es ist eher eine Frage von "Dies ist der bessere Weg, es zu tun" als alles, was Sie beeinflussen könnte. Es würde nur so etwas tun, das in einer ganzen Iteration millionenfach an dieser Funktion vorbeigeht.

6

Warum ist das?

Verschiedene Sequenzen von Anweisungen werden verwendet, wenn der Code vom JIT-Compiler (endlich) in nativen Code kompiliert wird. Ein Switch wird durch eine Sequenz nativer Anweisungen implementiert, die eine indirekte Verzweigung ausführen. (Die Sequenz lädt typischerweise eine Adresse aus einer Tabelle und verzweigt dann zu dieser Adresse.) Ein if/else wird als Anweisungen implementiert, die die Bedingung (wahrscheinlich einen Vergleichsbefehl), gefolgt von einem bedingten Verzweigungsbefehl, auswerten.

Warum 3?

Es ist eine empirische Beobachtung, nehme ich an Basis der generierten nativen Code Anweisungen zum Analysieren und/oder Benchmarking. (Oder möglicherweise nicht. Um absolut sicher zu sein, müssten Sie den Autor (die Autoren) dieser PMD-Regel fragen, wie sie diese Nummer abgeleitet haben.)

Wie definieren sie Effizienz?

Zeitaufwand für die Ausführung der Anweisungen.


Ich würde persönlich mit dieser Regel in Frage stellen ... oder genauer gesagt mit der Nachricht. Ich denke, es sollte sagen, dass eine Aussage einfacher und lesbarer ist als ein Schalter mit 2 Fällen. Das Effizienzproblem ist zweitrangig und wahrscheinlich irrelevant.