2010-08-31 12 views
13

Cuando me encuentro llamando al mismo método getter varias veces, ¿debería considerarse un problema? ¿Es mejor [siempre] asignar a una variable local y llamar solo una vez?Optimización prematura en Java: cuándo usar "x = foo.getX()" vs simplemente "foo.getX()"

Estoy seguro de que la respuesta por supuesto es "depende".

Estoy más preocupado por el caso más simple en el que el getter es simplemente un método de tipo "transmite el valor de una variable privada". es decir, no hay cálculos costosos involucrados, no se consumen las conexiones de bases de datos, etc.

Mi pregunta de "es mejor" se refiere a la legibilidad del código (estilo) y también al rendimiento. es decir, es que mucho de un impacto en el rendimiento que tienen:

SomeMethod1(a, b, foo.getX(), c); 
SomeMethod2(b, foo.getX(), c); 
SomeMethod3(foo.getX()); 

vs:

X x = foo.getX(); 
SomeMethod1(a, b, x, c); 
SomeMethod2(b, x, c); 
SomeMethod3(x); 

que dan cuenta de esta pregunta es un poco puntillosos y gris. Pero me acabo de dar cuenta, no tengo una manera consistente de evaluar estos intercambios, en absoluto. Estoy buscando algunos criterios que son más que completamente caprichosos.

Gracias.

+2

Este es un escenario en el que solo hay un hilo que se actualiza, supongo. – extraneon

+2

+1 para el uso de caprichoso en una pregunta –

+3

¿Es foo inmutable (al menos durante la vida útil de x)? Si no, entonces no son lo mismo. No es suficiente que getX no tenga efectos secundarios. Si SomeMethod1 invoca foo.setX, entonces no son lo mismo. Si no son lo mismo, en lugar de preocuparse por la legibilidad o el rendimiento, elija el correcto. – emory

Respuesta

14

La elección no debería ser realmente sobre el rendimiento alcanzado sino sobre la legibilidad del código.

Cuando crea una variable, puede darle el nombre que merece en el contexto actual. Cuando usa el mismo valor más de una vez, seguramente tiene un significado real, más que un nombre de método (o peor una cadena de métodos).
Y es realmente mejor leer:

String username = user.getName(); 
SomeMethod1(a, b, username, c); 
SomeMethod2(b, username, c); 
SomeMethod3(username); 

que

SomeMethod1(a, b, user.getName(), c); 
SomeMethod2(b, user.getName(), c); 
SomeMethod3(user.getName()); 
+2

IMO, 'user.getName()' es a veces más fácil de leer, porque no tiene que buscar (y desplazarse hacia arriba) a la posición donde se ha establecido. Probablemente también le pondrás nombre a la variable 'username', si fue establecida a través de' request.getUsername() '- entonces, ¿cuál es? –

+0

+1 para la legibilidad del código – extraneon

+0

@chris_l como dije, cada nombre de variable tiene su propio significado en el contexto donde se usa. Y sí, probablemente también nombraré 'request.getUsername()' username. Y si tuviera que usar ambos, habría una razón, como un nuevo nombre de usuario y uno anterior. newUsername, oldUsername podría caber. Se trata del contexto. –

2

La mayoría de las veces usaría getX si solo fuera una vez y creara una var para todos los demás casos. A menudo solo para guardar la escritura.

Con respecto al rendimiento, el compilador probablemente podría optimizar la mayor parte de la sobrecarga, pero la posibilidad de efectos secundarios podría forzar al compilador a trabajar más al hacer múltiples llamadas a métodos.

8

Para captadores simples, aquellos que simplemente devuelven un valor, HotSpot lo inscribe en el código de llamada, por lo que será tan rápido como sea posible.

Yo, sin embargo, tengo el principio de mantener una declaración en una sola línea, lo que muy a menudo hace que expresiones como "foo.getBar()" sean demasiado largas para caber. Entonces, para mí, es más legible extraerlo a una variable local ("Bar bar = foo.getBar()").

6

Pueden ser 2 cosas diferentes.

Si GetX es no determinista, entonces el primero uno va a dar resultados diferentes que el segundo

En lo personal, me gustaría usar el segundo uno. Es más obvio y menos innecesariamente detallado.

+3

La palabra que está buscando es "idempotente". 'x + = 2; return x; 'es completamente determinista, pero dará resultados diferentes cuando se lo llame varias veces. –

+0

@Michael Madsen: cierto. pensamiento descuidado por mi parte – gbn

3

Puedo usar el segundo estilo si se hace el código más legible o si tengo que usar el valor asignado de nuevo. Nunca considero el rendimiento (en cosas triviales) a menos que sea necesario.

+0

Además, puede ser más fácil establecer un punto de interrupción para X x = foo.getX(); para examinar el valor devuelto – RealHowTo

3

Eso depende de lo que getX() realmente hace. Considere esta clase:

public class Foo { 
    private X x; 

    public X getX() { return x; } 
} 

En este caso, cuando se hace una llamada a foo.getX(), JVM optimizará todo el camino hasta foo.x(como en referencia directa a foo 's ámbito privado, básicamente, un puntero de memoria). Sin embargo, si la clase es el siguiente:

public class Foo { 
    private X x; 

    public X getX() { return cleanUpValue(x); } 

    private X cleanUpValue(X x) { 
     /* some modifications/sanitization to x such as null safety checks */ 
    } 
} 

la JVM no puede en realidad inline la manera más eficiente ya que por más Foo 's contrato de construcción, se tiene que desinfectar x antes de entregarla a cabo.

En resumen, si getX() realmente no hace nada más que devolver un campo, entonces no hay diferencia después de que la optimización inicial se ejecute en el bytecode en si llama al método una o más veces.

+0

Si lo he entendido correctamente, HotSpot puede alinear el getX() incluso si llama más código. La decisión se basa en el tamaño del método, no en lo que realmente hace. –

+0

Parece que recuerdo haber leído algo en jruby que en este momento el Hotspot enuncia actualmente hasta 9 llamadas anidadas –

+0

La JVM no debería tener problemas en línea getX y cleanUpValue. –

1

Dos cosas tienen que ser considerados:

  1. ¿La llamada a getX() tiene efectos secundarios? Siguiendo los patrones de codificación establecidos, un getter no debe alterar el objeto al que se llama, en la mayoría de los casos, no hay ningún efecto secundario. Por lo tanto, es semánticamente equivalente llamar al getter una vez y almacenar el valor localmente contra llamar al getter varias veces. (Este concepto se denomina idempotencia: no importa si llama a un método una o varias veces, el efecto en los datos es exactamente el mismo).

  2. Si el captador no tiene ningún efecto secundario, el compilador puede eliminarlo con seguridad llamadas posteriores al comprador y crear el almacenamiento local temporal por sí mismo, por lo tanto, el código sigue siendo legible y tiene toda la ventaja de velocidad de llamar al getter solo una vez. Esto es aún más importante si el captador no solo devuelve un valor sino que tiene que buscar/calcular el valor o ejecutar algunas validaciones.

Asumiendo que su captador no cambia el objeto sobre el que opera es probable que sea más fácil de leer para tener varias llamadas a getX() - y gracias al compilador que no tiene que el desempeño del comercio para mejorar la legibilidad y facilidad de mantenimiento.

2

general I almacenar de forma local si:

  1. Estoy va a usar en un bucle y no quiero o espero que el valor cambie durante el bucle.

  2. Estoy a punto de usarlo en una larga línea de código o la función & Los parámetros son muy largos.

  3. Quiero cambiar el nombre de la variable para que se corresponda mejor con la tarea en cuestión.

  4. Las pruebas indican un aumento significativo en el rendimiento.

De lo contrario, me gusta la posibilidad de obtener valores actuales y un nivel más bajo de abstracción de las llamadas a métodos.

Cuestiones relacionadas