Soy un principiante y siempre he leído que es malo repetir código. Sin embargo, parece que para no hacerlo, normalmente tendría que tener llamadas a métodos adicionales. Digamos que tengo la siguiente clase
public class BinarySearchTree<E extends Comparable<E>>{
private BinaryTree<E> root;
private final BinaryTree<E> EMPTY = new BinaryTree<E>();
private int count;
private Comparator<E> ordering;
public BinarySearchTree(Comparator<E> order){
ordering = order;
clear();
}
public void clear(){
root = EMPTY;
count = 0;
}
}
¿Sería más óptimo para mí simplemente copiar y pegar las dos líneas en mi método clear () en el constructor en lugar de llamar al método real? Si es así, ¿qué diferencia hay? ¿Qué pasa si mi constructor realiza 10 llamadas a métodos y cada una simplemente establece una variable de instancia en un valor? ¿Cuál es la mejor práctica de programación?
java
performance
methods
call
jhlu87
fuente
fuente
Respuestas:
El compilador puede realizar esa optimización. Y también la JVM. La terminología utilizada por el redactor del compilador y los autores de JVM es "expansión en línea".
Mídelo. A menudo, encontrará que no hay ninguna diferencia. Y si cree que se trata de un punto de acceso de rendimiento, está buscando en el lugar equivocado; es por eso que necesitarás medirlo.
De nuevo, eso depende del código de bytes generado y de las optimizaciones de tiempo de ejecución realizadas por la máquina virtual Java. Si el compilador / JVM puede incorporar las llamadas al método, realizará la optimización para evitar la sobrecarga de crear nuevos marcos de pila en tiempo de ejecución.
Evitando la optimización prematura. La mejor práctica es escribir código legible y bien diseñado, y luego optimizar para los puntos calientes de rendimiento en su aplicación.
fuente
System.nanoTime()
oSystem.currentTimeMillis
es una mala forma de elaborar perfiles. Puede obtener una lista de perfiladores de la respuesta a esta pregunta de Stackoverflow . Recomendaría VisualVM, ya que ahora viene con el JDK.Lo que todos los demás han dicho sobre la optimización es absolutamente cierto.
No hay ninguna razón desde el punto de vista del rendimiento para incorporar el método. Si se trata de un problema de rendimiento, el JIT de su JVM lo integrará. En Java, las llamadas a métodos están tan cerca de ser gratuitas que no vale la pena pensar en ello.
Dicho esto, aquí hay un problema diferente. A saber, es mala práctica de programación para llamar a un método overrideable (es decir, uno que no lo es
final
,static
oprivate
) desde el constructor. (Efectivo Java, 2ª Ed., P. 89 en el ítem titulado "Diseñar y documentar para herencia o prohibirlo")¿Qué sucede si alguien agrega una subclase de
BinarySearchTree
llamadoLoggingBinarySearchTree
que anula todos los métodos públicos con un código como:public void clear(){ this.callLog.addCall("clear"); super.clear(); }
¡Entonces
LoggingBinarySearchTree
nunca se podrá construir! El problema es quethis.callLog
seránull
cuando elBinarySearchTree
constructor se esté ejecutando, pero elclear
que se llama es el anulado y obtendrá unNullPointerException
.Tenga en cuenta que Java y C ++ difieren aquí: en C ++, un constructor de superclase que llama a un
virtual
método termina llamando al definido en la superclase, no al anulado. Las personas que cambian entre los dos idiomas a veces lo olvidan.Dado eso, creo que probablemente sea más limpio en su caso incorporar el
clear
método cuando se llama desde el constructor , pero en general en Java debe seguir adelante y hacer todas las llamadas al método que desee.fuente
Definitivamente lo dejaría como está. ¿Y si cambia la
clear()
lógica? No sería práctico encontrar todos los lugares donde copió las 2 líneas de código.fuente
En términos generales (¡y como principiante esto significa siempre!), Nunca debes hacer microoptimizaciones como la que estás considerando. Siempre favorezca la legibilidad del código sobre cosas como esta.
¿Por qué? Porque el compilador / hotspot hará este tipo de optimizaciones sobre la marcha, y muchas, muchas más. En todo caso, cuando intente hacer optimizaciones a lo largo de este tipo de líneas (aunque no en este caso) probablemente hará las cosas más lentas. Hotspot entiende los modismos de programación comunes, si intenta hacer esa optimización usted mismo, probablemente no entenderá lo que está tratando de hacer, por lo que no podrá optimizarlo.
También hay un costo de mantenimiento mucho mayor. Si comienza a repetir el código, será mucho más difícil mantenerlo, ¡lo que probablemente será mucho más complicado de lo que cree!
Por otro lado, es posible que llegue a algunos puntos en su vida de codificación en los que necesite realizar optimizaciones de bajo nivel, pero si llega a esos puntos, definitivamente, definitivamente sabrá cuando llegue el momento. Y si no lo hace, siempre puede volver atrás y optimizar más tarde si es necesario.
fuente
La mejor práctica es medir dos veces y cortar una vez.
Una vez que haya perdido el tiempo en la optimización, ¡nunca podrá recuperarla! (Así que primero mídelo y pregúntate si vale la pena optimizarlo. ¿Cuánto tiempo real ahorrarás?)
En este caso, la máquina virtual Java probablemente ya esté realizando la optimización de la que está hablando.
fuente
El costo de una llamada a un método es la creación (y eliminación) de un marco de pila y algunas expresiones de código de bytes adicionales si necesita pasar valores al método.
fuente
El patrón que sigo es si este método en cuestión satisfaría o no uno de los siguientes:
Si algo de lo anterior es cierto, debe incluirse en su propio método.
fuente
i++;
Conserve el
clear()
método cuando ayude a la legibilidad. Tener un código que no se puede mantener es más caro.fuente
La optimización de los compiladores suele hacer un buen trabajo al eliminar la redundancia de estas operaciones "extra"; en muchos casos, la diferencia entre el código "optimizado" y el código simplemente escrito de la forma deseada y ejecutado a través de un compilador optimizador es nula; es decir, el compilador de optimización normalmente hace un trabajo tan bueno como tú, y lo hace sin causar ninguna degradación del código fuente. De hecho, muchas veces, el código "optimizado a mano" termina siendo MENOS eficiente, porque el compilador considera muchas cosas al hacer la optimización. Deje su código en un formato legible y no se preocupe por la optimización hasta más adelante.
fuente
No me preocuparía tanto por la llamada al método sino por la lógica del método. Si se tratara de sistemas críticos y el sistema necesitara "ser rápido", entonces buscaría optimizar los códigos que tardan mucho en ejecutarse.
fuente
Dada la memoria de las computadoras modernas, esto es muy económico. Siempre es mejor dividir el código en métodos para que alguien pueda leer rápidamente lo que está pasando. También ayudará a reducir los errores en el código si el error se limita a un solo método con un cuerpo de unas pocas líneas.
fuente
Como han dicho otros, el costo de la llamada al método es trivial a nada, ya que el compilador lo optimizará para usted.
Dicho esto, existen peligros al realizar llamadas a métodos a métodos de instancia desde un constructor. Corre el riesgo de actualizar posteriormente el método de instancia para que pueda intentar utilizar una variable de instancia que aún no haya sido iniciada por el constructor. Es decir, no necesariamente desea separar las actividades de construcción del constructor.
Otra pregunta: su método clear () establece la raíz en EMPTY, que se inicializa cuando se crea el objeto. Si luego agrega nodos a EMPTY y luego llama a clear (), no restablecerá el nodo raíz. ¿Es este el comportamiento que quieres?
fuente