2011-02-07 14 views
9

No he tenido mucha suerte obteniendo una respuesta concisa para esta comparación al usar Google y en lugar de hacer mis propias evaluaciones que consumen mucho tiempo, pensé que podría preguntar primero.Java: Desempeño de Enumerados contra if-then-else

Estoy bastante seguro de que una instrucción switch que utiliza Enums funcionaría más rápido que una instrucción if-then-else, aunque si es una diferencia notoria o no es otra cuestión.

¿Alguien podría arrojar algo de luz sobre esto para mí?


Gracias por las respuestas rápidas chicos, lo tendré en cuenta para futuros proyectos.

+2

Dudo mucho que la diferencia de rendimiento sea algo más que la micro optimización. Me decantaría por el conmutador stmt si proporciona un código más legible y fácil de mantener en su caso. – CoolBeans

+0

Tengo curiosidad, ¿qué te hace decir "estoy bastante seguro de que una instrucción de cambio que usa Enums funcionaría más rápido que una declaración if-then-else"? – Pops

+0

@Lord Torgamus: en una instrucción de conmutación, cada caso es verdadero o falso, en una instrucción if-then-else, cada uno si podría tener múltiples valores booleanos, es decir, this && that, o esto || eso, etc. Y me imagino que evaluar más de uno llevaría un poco más de tiempo. – FizzBuzz

Respuesta

8

Yeap, lo hace, porque en términos generales una instrucción switch funciona más rápido que if/else chain.

Aunque el bytecode generado no siempre es una fuente definitiva para las comparaciones de rendimiento, puede examinarlo para tener una mejor idea.

Por ejemplo, este código:

class A { 
    enum N { ONE, TWO, THREE } 
    void testSwitch(N e) { 
     switch(e) { 
      case ONE : x(); break; 
      case TWO : x(); break; 
      case THREE : x(); break; 
     } 
    } 
    void testIf(Enum e) { 
     if(e == N.ONE) { x(); } 
     else if(e == N.TWO) { x(); } 
     else if(e == N.THREE) { x(); } 
    } 
    void x(){} 
} 

genera los siguientes:

Compiled from "A.java" 
class A extends java.lang.Object{ 
A(); 
    Code: 
    0: aload_0 
    1: invokespecial #1; //Method java/lang/Object."<init>":()V 
    4: return 

void testSwitch(A$N); 
    Code: 
    0: getstatic #2; //Field A$1.$SwitchMap$A$N:[I 
    3: aload_1 
    4: invokevirtual #3; //Method A$N.ordinal:()I 
    7: iaload 
    8: tableswitch{ //1 to 3 
     1: 36; 
     2: 43; 
     3: 50; 
     default: 54 } 
    36: aload_0 
    37: invokevirtual #4; //Method x:()V 
    40: goto 54 
    43: aload_0 
    44: invokevirtual #4; //Method x:()V 
    47: goto 54 
    50: aload_0 
    51: invokevirtual #4; //Method x:()V 
    54: return 

void testIf(java.lang.Enum); 
    Code: 
    0: aload_1 
    1: getstatic #5; //Field A$N.ONE:LA$N; 
    4: if_acmpne 14 
    7: aload_0 
    8: invokevirtual #4; //Method x:()V 
    11: goto 39 
    14: aload_1 
    15: getstatic #6; //Field A$N.TWO:LA$N; 
    18: if_acmpne 28 
    21: aload_0 
    22: invokevirtual #4; //Method x:()V 
    25: goto 39 
    28: aload_1 
    29: getstatic #7; //Field A$N.THREE:LA$N; 
    32: if_acmpne 39 
    35: aload_0 
    36: invokevirtual #4; //Method x:()V 
    39: return 

void x(); 
    Code: 
    0: return 

} 

que parece ser bastante rápido en ambos casos.

Elija el que sea más fácil de mantener.

+0

aunque los conmutadores suelen ser más rápidos, un ejemplo de cuando un conmutador no es tan bueno es cuando las declaraciones de casos son dispersas y/o no están en orden, entonces puede perder sus ventajas ... pero como siempre con cualquier micro optimización, las pruebas de tiempo son la única respuesta verdadera :-) – SteelBytes

+0

Tres valores de enumeración no van a hacer una diferencia. Pruebe con 100 y verá una búsqueda de tabla frente a 100 comparaciones. – OrangeDog

+0

@Orange E invocar un método diferente en cada uno, por supuesto. – OscarRyz

1

No sé lo que es más rápido, supongo que ambos son tremendamente rápidos.

Mi consideración es que el interruptor con enumeraciones es mucho más fácil de leer que un multi-if/else bloquear

Pero cuidado de perder las sentencias break !!

0

En teoría, una instrucción switch puede optimizarse como un solo salto calculado, mientras que las cadenas if-then-else tienen que permanecer como comparaciones individuales. Sin embargo, no sé si Java realmente realiza esta optimización.

Independientemente, los interruptores son mejores que las cadenas if-then-else en términos de legibilidad y facilidad de mantenimiento, así que utilícelos de todos modos si es posible.

+0

De la memoria realiza este salto calculado (puede hacerlo fácilmente porque restringe los tipos que se pueden usar en un cambio a primitivas, enumeraciones y (en Java 7 y más adelante) cadenas. – berry120

+0

@ berry120 - Estaba pensando en el optimización óptimo (almohadilla los casos con NOP luego saltar hacia adelante 'enum.ordinal() * sizeOfCase'). – OrangeDog

1

Sí, una instrucción de conmutación casi siempre se ejecutará más rápido que el bloque equivalente de sentencias if/else porque el compilador puede realizar más optimizaciones (normalmente un bloque de conmutadores se compila en una tabla de ramificación que es prácticamente imposible de realizar con un bloque de condicionales.)

yo diría que también son tanto más legible y más fácil de mantener (con la excepción de la utilización de los casos con caídas a través del cual te aconsejo!)

en cuanto a si es notablemente más rápido, depende de lo que defina como notable. Lo más probable es que a menos que persigas algo realmente específico, no lo notes en absoluto, pero aún así haré las cosas por la ventaja de legibilidad más que cualquier otra cosa (¡trata la ventaja de velocidad como un bono!)

+0

"(típicamente un bloque de interruptores se convierte compilado a una tabla de rama que es casi imposible de hacer con un bloque de condicionales.) "- pero si el compilador es lo suficientemente inteligente como para ver que es solo una lista de variables en comparación con las constantes (numéricas) (que son las enumeraciones, en el fondo), ¿no sería capaz de optimizarlo como si fuera un ? switch/case – fwielstra

+0

@Cthulhu - yo he dicho que es imposible ver con el caso general de un bloque de condicionales Sí, en el caso que usted describe en teoría sería, aunque no estoy seguro de que el compilador hace realmente t. su análisis (IMO hay mejores optimizaciones en las que vale la pena concentrarse que este de todos modos). – berry120

6

Solo con el código más legible y fácil de entender que se te pueda ocurrir, estoy seguro de que has perdido todo el tiempo ganado en la optimización del rendimiento mientras buscabas esta respuesta.Las micro optimizaciones como esta rara vez valen la pena y pueden dar como resultado fácilmente un código que es más complejo de lo necesario.

1

Mi respuesta a esto es la misma que siempre a la pregunta es el lenguaje construir X generalmente más rápido que el lenguaje construir Y: ¡No hay una respuesta general!

Sólo puede haber una respuesta específica para ciertas implementaciones de un idioma, por ejemplo, La JVM basada en el compilador de Hotspot de Oralce (formalmente Sun) o para un JDK de IBM en plataforma Z o para un OpenJDK en Linux, o ...

Entonces, la única manera de dar una respuesta significativa a su pregunta es hacer una punto de referencia adecuado. Tenga cuidado con los micro benchmarks, son más a menudo incorrectos que correctos, vea p. How not to write a micro benchmark. Si aún desea obtener información sobre el uso de esta pregunta framework se describe here.

Por lo tanto, aconsejaría seleccionar la característica de idioma por su aplicabilidad y legibilidad en su contexto.