2010-03-08 22 views
36

escribí dos métodos para comprobar su desempeño para'...! = Null' o 'null! = ....' mejor rendimiento?

public class Test1 { 

private String value; 

public void notNull(){ 
    if(value != null) { 
    //do something 
    } 
} 

public void nullNot(){ 
if(null != value) { 
    //do something 
} 
} 

} 

y comprueba que es el código de bytes después de compilar

public void notNull(); 
Code: 
Stack=1, Locals=1, Args_size=1 
0: aload_0 
1: getfield #2; //Field value:Ljava/lang/String; 
4: ifnull 7 
7: return 
LineNumberTable: 
line 6: 0 
line 9: 7 

StackMapTable: number_of_entries = 1 
frame_type = 7 /* same */ 


public void nullNot(); 
Code: 
Stack=2, Locals=1, Args_size=1 
0: aconst_null 
1: aload_0 
2: getfield #2; //Field value:Ljava/lang/String; 
5: if_acmpeq 8 
8: return 
LineNumberTable: 
line 12: 0 
line 15: 8 

StackMapTable: number_of_entries = 1 
frame_type = 8 /* same */ 


} 

aquí dos códigos de operación se utilizan para implementar la condición if: en el primer caso se utilizan ifnull- comprobar que el valor superior de la pila es nulo-, y en el segundo caso usar if_acmpeq- comprobar que los dos valores superiores son iguales en la pila-

, ¿afectará esto al rendimiento? (esto me ayudará a probar que la primera implementación de null es buena en cuanto a rendimiento y legibilidad :))

+4

La respuesta de polygene es la que debes escuchar. pero si realmente cree que la diferencia es importante, ejecute cada versión un billón de veces más o menos, y vea si hay alguna diferencia mensurable. luego regresa y nos cuenta sobre eso. –

+0

¿Por qué es importante? ¿Su programa tiene solo 3 líneas de longitud, por lo que importa tanto qué tan rápido o lento se ejecute una única instrucción if? –

+7

Mientras estamos en el tema, me gustaría probar que poner la llave de apertura en la misma línea que el condicional es más eficiente que otros estilos de corsé. –

Respuesta

63

La comparación de los códigos de bytes generados es casi irrelevante, ya que la mayor parte de la optimización ocurre en tiempo de ejecución con el compilador JIT. Voy a adivinar que en este caso, cualquiera de las expresiones es igual de rápida. Si hay alguna diferencia, es insignificante.

Esto no es algo de lo que deba preocuparse. Busque optimizaciones de imágenes grandes.

+6

+1 para distinguir entre bytecode y ensamblaje: esta es una distinción muy importante a tener en cuenta. – Cam

+4

Realmente es bastante simple: si uno fuera más rápido que el otro, algunos tipos inteligentes de Microsoft habrían * ya * hecho que el compilador o el JIT convirtieran el lento en uno rápido. –

+0

Buen punto en bytecodes, pero ¿cómo sabes que esto no es una optimización de imagen grande? Si está buscando datos poco poblados, la verificación de nulos podría ser el lugar donde su código pasa la mayor parte del tiempo. –

1

Poner null primero parece generar un código de byte extra, pero aparte de eso, puede que no haya una diferencia en el rendimiento.

Personalmente, no me preocuparía el rendimiento hasta que sea hora de preocuparse por el rendimiento.

Usaría el enfoque notNull(), solo para que no arroje un error de compilación si olvida el ! y accidentalmente ingresa null = value.

+0

pero en el caso en condición if si comete el mismo error, no se compilará, si no es de tipo booleano escriba – asela38

+1

Creo que es más probable que cometa un error al escribir 'notNull (value)' que 'value ! = null'. – Ponkadoodle

+0

Sí, estoy de acuerdo, quise hablar en general al realizar una verificación de tipo, aunque no lo dejé claro. Gracias. –

15

No optimice a expensas de la legibilidad si la ganancia de velocidad (o memoria/en su caso) sea despreciable. Creo que !=null es generalmente más legible, así que úselo.

+0

Estoy de acuerdo con lo que dices sobre la legibilidad. null es constante, y para las comparaciones que incluyen constantes 'variable constante' es la más legible. – Ponkadoodle

+4

... a menos que lea de derecha a izquierda – CodyBugstein

3

La diferencia será insignificante así que ve con lo que es más legible (!= null OMI)

1

Oh, si solicita el máximo rendimiento, no crean clase o métodos adicionales. Incluso los métodos estáticos tomarían un poco de tiempo ya que el cargador de clases Java necesita cargarlo JIT.

lo tanto, siempre es necesario comprobar si una variable es nula, sólo lo prueba por cualquiera

if (x == null) 

o

if (null == x) 

Francamente Calculo del bono de desempeño que elegir uno de los dos es fácilmente compensado por la sobrecarga de la introducción de métodos innecesarios.

+0

Estoy seguro de que estos métodos solo se presentaron como una forma fácil de ver la diferencia en bytecode generado. –

2

Me quedaría con (valor! = Nulo) para la legibilidad. Pero siempre puedes usar Assertions.

5

Aparte de la sabiduría duramente ganada de evitar la asignación accidental en C, que favorece poner la constante a la izquierda del operador binario, la constante a la izquierda es más legible porque pone el valor crucial en la posición más prominente.

Por lo general, un cuerpo de función utilizará solo unas pocas variables, y generalmente es evidente a través del contexto qué variable está bajo inspección. Al poner la constante a la izquierda, imitamos más de cerca switch y case: dado esta variable, seleccione un valor coincidente. Al ver el valor de la izquierda, uno se enfoca en la condición particular que se selecciona.

Cuando escaneo

if (var == null) 

lo leí como, "Estamos inspeccionando var aquí, y estamos comparándolo por la igualdad, contra ... ah, nulo." Por el contrario, al realizar una búsqueda

if (null == var) 

pienso: "Estamos viendo si un valor es nulo, y ... sí, es var estamos inspeccionando." Es un reconocimiento aún más fuerte con

if (null != var) 

que mi ojo simplemente retoma inmediatamente.

Esta intuición proviene de la consistencia del hábito, prefiriendo leer lo que uno escribe y escribir lo que uno prefiere leer. Uno puede aprender de cualquier manera, pero no es objetivamente cierto, ya que otros han respondido aquí que poner la variable a la izquierda es más claro. Depende de qué aspecto de la expresión uno quiera ser más claro primero.

Ver la diferencia de bytecode fue fascinante. Gracias por compartir eso.

+17

Para cada uno su propia intuición ... (Aunque definitivamente está equivocado acerca de esto;;)) –

+4

La 'sabiduría duramente ganada de evitar la asignación accidental en C' está alrededor de 20 años desactualizada, ya que los compiladores de C ahora producen advertencias sobre eso (en lugar de tener que obtenerlos de 'lint'), y en realidad no se aplica a Java. – EJP

+0

Que los compiladores adviertan acerca de esto ahora no cambia la razón por la cual los programadores antiguos pueden haber adoptado este estilo por primera vez, pero eso no importa. Mi punto aquí es que evitar esa asignación accidental no es el único beneficio del estilo, y todavía hay buenas razones para adoptarlo hoy. Dependiendo de lo que más le interese al lector, este estilo puede "leer mejor". – seh

2

La optimización de minutos como esa es el trabajo del compilador, especialmente en lenguajes de alto nivel como Java.

Aunque estrictamente no es relevante aquí, ¡no optimice prematuramente!

9

Con preguntas como esta, es difícil saber qué tan inteligente será la JVM (aunque la respuesta es "por lo general bastante inteligente si es posible" y parece muy posible en este caso). Pero sólo para estar seguro, probarlo:

class Nullcheck { 
    public static class Fooble { } 

    Fooble[] foo = {null , new Fooble(), null , null, 
        new Fooble(), null, null, new Fooble() }; 

    public int testFirst() { 
    int sum = 0; 
    for (int i=0 ; i<1000000000 ; i++) if (foo[i&0x7] != null) sum++; 
    return sum; 
    } 

    public int testSecond() { 
    int sum = 0; 
    for (int i=0 ; i<1000000000 ; i++) if (null != foo[i&0x7]) sum++; 
    return sum; 
    } 

    public void run() { 
    long t0 = System.nanoTime(); 
    int s1 = testFirst(); 
    long t1 = System.nanoTime(); 
    int s2 = testSecond(); 
    long t2 = System.nanoTime(); 
    System.out.printf("Difference=%d; %.3f vs. %.3f ns/loop (diff=%.3f)\n", 
     s2-s1,(t1-t0)*1e-9,(t2-t1)*1e-9,(t0+t2-2*t1)*1e-9); 
    } 

    public static void main(String[] args) { 
    Nullcheck me = new Nullcheck(); 
    for (int i=0 ; i<5 ; i++) me.run(); 
    } 
} 

Y en mi máquina esta rendimientos:

Difference=0; 2.574 vs. 2.583 ns/loop (diff=0.008) 
Difference=0; 2.574 vs. 2.573 ns/loop (diff=-0.001) 
Difference=0; 1.584 vs. 1.582 ns/loop (diff=-0.003) 
Difference=0; 1.582 vs. 1.584 ns/loop (diff=0.002) 
Difference=0; 1.582 vs. 1.582 ns/loop (diff=0.000) 

Así que la respuesta es: no, no hubo diferencia significativa en absoluto. (Y el compilador JIT puede encontrar trucos adicionales para acelerar cada uno hasta después de que el mismo número de carreras de repetición.)


actualización: El código anterior se ejecuta un punto de referencia ad-hoc. Usar JMH (¡ahora que existe!) Es una buena manera de ayudar a evitar (algunas) trampas de microencuadres. El código anterior evita las peores trampas pero no da estimaciones de error explícitas e ignora otras varias cosas que a veces importan. En estos días: ¡usa JMH! Además, en caso de duda, ejecute sus propios puntos de referencia. Los detalles a veces importan, no muy a menudo, por algo tan sencillo como esto, pero si es realmente importante para usted, debe verificar una condición lo más cercana posible a la producción.

1

puede pasar por alto estas cosas muy optimización minutos durante la codificación

1

Como se puede ver el rendimiento diferente es muy inferior. No se preocupe por las cosas pequeñas, siempre es mejor enfocarse más en el algoritmo.Y, obviamente, la legibilidad es un factor.

-2

El código de bytes es solo una traducción simple del código fuente.

+0

Eso depende completamente del compilador que está utilizando. – EJP

1

Desde el punto de vista, no hay una diferencia significativa en el rendimiento.

Sin embargo, es útil escribir el nulo primero para detectar errores de tipografía.

Por ejemplo, si estás acostumbrado a escribir este código:

if (obj == null) 

Podría ser escribió por error como:

if (obj = null) 

Desde el punto de vista del compilador, esto está muy bien.

Sin embargo, si estás acostumbrado a escribir el código como:

if (null == obj) 

y cometió el error de escribir:

if (null = obj) 

el compilador le permitirá saber que cometió un error en esa línea .

+2

Esto ya ha sido debatido, ver [comentarios en otra respuesta] (http://stackoverflow.com/a/2398688/224132). Debe editar esta respuesta para explicar por qué cree que es útil, a pesar de que los comentadores dicen que no. –

Cuestiones relacionadas