En el caso de un código en el que debe realizar una limpieza de recursos antes de salir de una función, ¿existe una diferencia de rendimiento importante entre estas 2 formas de hacerlo?
Limpieza del recurso antes de cada declaración de devolución
void func() { login(); bool ret = dosomething(); if(ret == false) { logout(); return; } ret = dosomethingelse(); if(ret == false) { logout(); return; } dootherstuff(); logout(); }
Limpiar el recurso en un bloque finalmente
void func() { login(); try { bool ret = dosomething(); if(ret == false) return; ret = dosomethingelse(); if(ret == false) return; dootherstuff(); } finally { logout(); } }
Hice algunas pruebas básicas en programas de muestra y no parece haber mucha diferencia. Prefiero mucho la finally
forma de hacer esto, pero me preguntaba si causará algún impacto en el rendimiento en un gran proyecto.
java
performance
efficiency
usuario93353
fuente
fuente
if(!cond)
, entonces es java lo que me hizo hacer esto. En C ++, así es como escribo código de booleanos y también para otros tipos, es decirint x;
if(!x)
. Como java me permite usar esto solo parabooleans
, he dejado de usarif(cond)
&if(!cond)
en java.(someIntValue != 0)
que comparar en lugar de evaluar booleanos. Eso me huele, y lo refactorizo inmediatamente cuando lo veo en la naturaleza.Respuestas:
Como se indica en ¿Qué tan lentas son las excepciones de Java? se puede ver que la lentitud
try {} catch {}
está dentro de la instauración de la excepción misma.Crear una excepción obtendrá toda la pila de llamadas del tiempo de ejecución y aquí es donde está el gasto. Si no está creando una excepción, esto es solo un aumento de tiempo muy leve.
En el ejemplo dado en esta pregunta no hay excepciones, uno no esperaría ninguna desaceleración al crearlas, no se crean. En cambio, lo que está aquí es
try {} finally {}
manejar la desasignación de recursos dentro del bloque finalmente.Entonces, para responder a la pregunta, no, no hay un gasto real de tiempo de ejecución dentro de una
try {} finally {}
estructura que no use excepciones (como se ve). Lo que posiblemente sea costoso es el tiempo de mantenimiento cuando uno lee el código y ve que este estilo de código no es típico y el codificador tiene que pensar que algo más sucede en este método después dereturn
antes de regresar a la llamada anterior.Como se ha mencionado, el mantenimiento es un argumento para ambas formas de hacerlo. Para el registro, después de considerarlo, mi preferencia sería el enfoque final.
Considere el tiempo de mantenimiento de enseñarle a alguien una nueva estructura de lenguaje. Ver
try {} finally {}
no es algo que a menudo se ve en el código Java y, por lo tanto, puede ser confuso para las personas. Hay un grado de tiempo de mantenimiento para aprender estructuras un poco más avanzadas en Java que las que la gente conoce.El
finally {}
bloque siempre corre. Y es por eso que deberías usarlo. Considere también el tiempo de mantenimiento de la depuración del enfoque no definitivo cuando alguien olvida incluir un cierre de sesión en el momento adecuado, o lo llama en el momento incorrecto, u olvida regresar / salir después de llamarlo para que se llame dos veces. Hay tantos errores posibles con esto que el uso detry {} finally {}
hace imposible tenerlos.Al sopesar estos dos costos, es costoso en tiempo de mantenimiento no utilizar el
try {} finally {}
enfoque. Si bien las personas pueden analizar cuántas milisegundos fraccionales o instrucciones jvm adicionalestry {} finally {}
se compara el bloque con la otra versión, también se debe considerar las horas dedicadas a la depuración de la forma menos que ideal de abordar la desasignación de recursos.Escriba primero el código que se pueda mantener, y preferiblemente de una manera que evite que los errores se escriban más tarde.
fuente
/programming/299068/how-slow-are-java-exceptions
La respuesta aceptada en esta pregunta muestra que envolver una llamada de función en un bloque try catch cuesta menos del 5% sobre una llamada de función simple. En realidad, lanzar y capturar la excepción causó que el tiempo de ejecución se disparara a más de 66 veces la llamada a la función desnuda. Entonces, si espera que el diseño de excepción se lance regularmente, trataría de evitarlo (en un código crítico de rendimiento correctamente perfilado), sin embargo, si la situación excepcional es rara, entonces no es un gran problema.
fuente
En lugar de optimizar, piense en lo que está haciendo su código.
Agregar el último bloque es una implementación diferente: significa que cerrará sesión en cada excepción.
Específicamente: si el inicio de sesión arroja una excepción, el comportamiento en su segunda función será diferente de la primera.
Si el inicio de sesión y el cierre de sesión no son realmente parte del comportamiento de la función, sugeriría que el método debería hacer una cosa y una cosa bien:
y debe estar en una función que ya esté encapsulada en un contexto que administre el inicio / cierre de sesión, ya que estos parecen fundamentalmente diferentes de lo que está haciendo su código principal.
Su publicación está etiquetada como Java con la que no estoy muy familiarizado, pero en .net usaría un bloque de uso para esto:
es decir:
Y el contexto de inicio de sesión tiene un método Dispose que cerrará sesión y limpiará los recursos.
Editar: ¡en realidad no respondí la pregunta!
No tengo evidencia medible, pero no esperaría que esto sea más costoso o ciertamente no lo suficientemente costoso como para ser digno de una optimización prematura.
Voy a dejar el resto de mi respuesta porque, aunque no responda la pregunta, creo que es útil para el OP.
fuente
using
construcción .net , suponiendo que el recurso en cuestión implemente laAutoCloseable
interfaz.ret
variable, que solo se asigna dos veces para que se verifique una línea más tarde. Hacerloif (dosomething() == false) return;
.ret2 = doSomethingElse(ret1)
, pero eso no es relevante para la pregunta, por lo que se eliminó.if (dosomething() == false) ...
Es un mal código. Use el más intuitivoif (!dosomething()) ...
Suposición: está desarrollando en código C #.
La respuesta rápida es que no hay un impacto significativo en el rendimiento al usar los bloques try / finally. Hay un golpe de rendimiento cuando lanzas y atrapas excepciones.
La respuesta más larga es ver por ti mismo. Si observa el código CIL subyacente generado ( por ejemplo, el reflector de puerta roja, puede ver las instrucciones CIL subyacentes generadas y ver el impacto de esa manera.
fuente
Como otros ya se ha señalado, el código Ttese tendrá resultados diferentes, si cualquiera
dosomethingelse
odootherstuff
lanzar una excepción.Y sí, cualquier llamada de función puede arrojar una excepción, al menos un
StackOVerflowError
! Si bien es poco probable manejarlos correctamente (ya que cualquier función llamada limpieza probablemente tenga el mismo problema, en algunos casos (como implementar bloqueos, por ejemplo) es crucial incluso manejar eso correctamente ...fuente