java - programacion - Eficiencia: cambiar las declaraciones si las declaraciones
imagenes java eclipse (4)
PMD
me dice
Un interruptor con menos de 3 ramas es ineficiente, use una instrucción if en su lugar.
¿Porqué es eso? ¿Por qué 3? ¿Cómo definen la eficiencia?
¿Porqué es eso?
Se utilizan diferentes secuencias de instrucciones cuando el compilador JIT compila (finalmente) el código nativo en código nativo. Un interruptor se implementa mediante una secuencia de instrucciones nativas que realizan una rama indirecta. (La secuencia generalmente carga una dirección de una tabla y luego se bifurca a esa dirección). Un if / else se implementa 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 que en base al análisis de las instrucciones de código nativo generadas y / o la evaluación comparativa. (O posiblemente no. Para estar absolutamente seguro, debe preguntar al / los autor (es) de esa regla de PMD cómo derivaron ese número).
¿Cómo definen la eficiencia?
Tiempo necesario para ejecutar las instrucciones.
Personalmente, estoy en desacuerdo con esta regla ... o más precisamente con el mensaje. Creo que debería decir que una sentencia if / else
es más simple y más legible que un switch con 2 casos. El problema de la eficiencia es secundario, y probablemente irrelevante.
Aunque hay ganancias menores de eficiencia cuando se usa un interruptor en comparación con el uso de una sentencia if, esas ganancias serían despreciables en la mayoría de las circunstancias. Y cualquier escáner de código fuente que valga la pena reconocería que micro-optimizations son secundarias a la claridad del código.
Están diciendo 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 interruptor es significativamente corto.
Desde el sitio web de PMD :
TooFewBranchesForASwitchStatement: las instrucciones de cambio se utilizan para admitir comportamientos de bifurcación complejos. El uso de un conmutador para solo unos pocos casos no es aconsejable, ya que los conmutadores no son tan fáciles de entender como las afirmaciones if-then. En estos casos, use la declaración theif-then para aumentar la legibilidad del código.
Creo que tiene que ver con la forma en que se compila un switch y if / else.
Digamos que se necesitan 5 cálculos para procesar una instrucción de cambio. Digamos que una sentencia if requiere dos cálculos. Menos de 3 opciones en su conmutador equivaldrían a 4 cálculos en ifs frente a 5 en conmutadores. Sin embargo, la sobrecarga permanece constante en un conmutador, por lo que si tiene 3 opciones, ifs se procesaría 3 * 2, y 5 aún 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 afectarte. Solo lo haría en algo que realice ciclos en esa función millones de veces en una iteración bastante.
Debido a que una instrucción de switch
se compila con dos instrucciones especiales de JVM que son el lookupswitch
y el tableswitch
. Son útiles cuando se trabaja con muchos casos, pero causan una sobrecarga cuando tiene pocas sucursales.
En if/else
lugar, una declaración if/else
se compila en las típicas cadenas de je
jne
... que son más rápidas pero que requieren muchas más comparaciones cuando se usan en una larga cadena de sucursales.
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, entonces 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