2012-05-05 24 views
29

PMD me diceEficiencia: Interruptor declaraciones sobre si las declaraciones

Un interruptor con menos de 3 ramas es ineficiente, el uso de una sentencia if lugar.

¿Por qué es eso? ¿Por qué 3? ¿Cómo definen la eficiencia?

+2

¿Qué es PMD? ? ? – jmort253

+3

PMD escanea el código fuente de Java y busca posibles problemas como posibles errores, código muerto, código subóptimo, expresiones complicadas y código duplicado. (Desplácese sobre las etiquetas) – Chords

+9

También debe escanearse para ver la gramática. "Menos" debería ser "menos". :) – yshavit

Respuesta

36

Porque una instrucción switch se compila con dos instrucciones especiales de JVM que son lookupswitch y tableswitch. Son útiles cuando se trabaja con muchos casos, pero causan una sobrecarga cuando tienes pocas ramas.

En su lugar, una declaración if/else se compila en las típicas jejne ... cadenas que son más rápidas pero requieren muchas más comparaciones cuando se usan en una larga cadena de ramas.

Puede ver la diferencia mirando el código de bytes, en cualquier caso no me preocuparía por estos problemas, si algo pudiera convertirse en un problema, JIT se encargará de ello.

ejemplo práctico:

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

se compila en:

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 

Mientras

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

se compila en

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

Gracias Jack. Esta es una respuesta maravillosa. En una nota lateral, ¿qué usaste para ver los archivos '.class'? – JAM

+0

Es un complemento para Eclipse, si no recuerdo mal debería ser éste: http://andrei.gmxhome.de/bytecode/index.html – Jack

+1

@JAM: creo que también puede usar [javap] (http: // docs.oracle.com/javase/1.5.0/docs/tooldocs/windows/javap.html). – RanRag

7

Aunque hay ganancias de eficiencia menores al usar un conmutador en comparación con el uso de un enunciado if, esas ganancias serían insignificantes en la mayoría de las circunstancias. Y cualquier escáner de código fuente que valga la pena reconocerá que micro-optimizations son secundarios a la claridad del código.

Dicen que una instrucción if es más fácil de leer y ocupa menos líneas de código que una instrucción switch si el cambio es significativamente corto.

Desde el PMD website:

TooFewBranchesForASwitchStatement: declaraciones del interruptor son indentado para ser utilizado para apoyar el comportamiento de ramificación compleja. No es aconsejable utilizar un conmutador en unos pocos casos, ya que los conmutadores no son tan fáciles de entender como las declaraciones if-then. En estos casos, use la instrucción if-then para aumentar la legibilidad del código.

+1

La importancia de la claridad supera con creces cualquier microoptimización. No hay duda de eso. – JAM

+5

Lo bueno es que dicen "ineficaz" en la advertencia, pero afirman que solo está relacionado con la "legibilidad" en la documentación. Buena coherencia. – Jack

1

Creo que tiene que ver con la forma en que se compila un interruptor y un if/else.

Digamos que se necesitan 5 cálculos para procesar una declaración de cambio. Digamos que una instrucción if toma dos cálculos. Menos de 3 opciones en su interruptor equivaldrían a 4 cómputos en ifs vs 5 en switches. Sin embargo, la sobrecarga permanece constante en un conmutador, por lo que si tiene 3 opciones, ifs sería 3 * 2 procesado, vs 5 todavía para el conmutador.

Las ganancias al observar millones de cálculos son extremadamente insignificantes. Es más una cuestión de "esta es la mejor manera de hacerlo" en lugar de cualquier cosa que pueda afectarlo. Solo lo haría en algo que ciclos de esa función millones de veces en una iteración bastante.

6

¿Por qué es eso?

Se utilizan secuencias de instrucciones diferentes cuando el código (finalmente) se compila en el código nativo por el compilador JIT. Un interruptor se implementa mediante una secuencia de instrucciones nativas que realizan una rama indirecta. (La secuencia normalmente carga una dirección de una tabla y luego se bifurca a esa dirección). Se implementa un if/else como instrucciones que evalúan la condición (probablemente una instrucción de comparación) seguida de una instrucción de bifurcación condicional.

¿Por qué 3?

Es una observación empírica, supongo, basada en el análisis de las instrucciones del código nativo generado y/o la evaluación comparativa. (O posiblemente no. Para estar absolutamente seguro, necesitaría preguntar al autor (es) de esa regla PMD cómo derivaron ese número.)

¿Cómo definen la eficiencia?

Tiempo empleado para ejecutar las instrucciones.


Yo personalmente tengo un problema con esta regla ... o más precisamente con el mensaje. Creo que debería decir que una declaración if/else es más simple y más legible que un interruptor con 2 casos. El problema de la eficiencia es secundario, y probablemente irrelevante.

Cuestiones relacionadas