es intentar-finalmente costoso

14

En el caso de un código en el que tenga que realizar una limpieza de recursos antes de salir de una función, ¿existe una gran diferencia de rendimiento entre estas dos formas de hacerlo?

  1. Limpiando el 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();
    }
    
  2. Limpiando 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 ejemplo y no parece haber mucha diferencia. Prefiero la forma finally de hacer esto, pero me preguntaba si causará algún impacto en el rendimiento de un proyecto grande.

    
pregunta user93353 04.09.2013 - 15:09

5 respuestas

22

Como se indica en ¿Qué tan lentas son las excepciones de Java? se puede ver que la lentitud of try {} catch {} está dentro de la instancia de la excepción.

La creación de una excepción recuperará 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 muy un poco más de tiempo.

En el ejemplo dado en esta pregunta no hay excepciones, uno no esperaría que se produjera una ralentización al crearlas, no se crearon. En su lugar, lo que hay aquí es un try {} finally {} para manejar la desasignación de recursos dentro del bloque finally.

Entonces, para responder a la pregunta, no, no hay un gasto de tiempo de ejecución real dentro de una estructura try {} finally {} que no use excepciones (no es inaudito, como se ve). Lo que es posiblemente costoso es el tiempo de mantenimiento cuando uno lee el código y ve este no es el estilo de código típico y el programador tiene que tener en cuenta que algo más sucede en este método después de return antes de regresar a la convocatoria anterior.

Como se ha mencionado, el mantenimiento es un argumento para ambas formas de hacer esto. Para que quede constancia, 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 uno ve a menudo en el código Java y, por lo tanto, puede ser confuso para las personas. Hay un tiempo de mantenimiento para aprender estructuras un poco más avanzadas en Java que lo que las personas están familiarizadas con ver.

El bloque finally {} siempre se ejecuta. Y es por eso que debes usarlo. Considere también el tiempo de mantenimiento de la depuración del enfoque no final cuando alguien se olvida de incluir un cierre de sesión en el momento adecuado, o lo llama en el momento incorrecto, o se olvida de regresar / salir después de llamar para que se llame dos veces. Hay tantos errores posibles con esto que es imposible tener el uso de try {} finally {} .

Al sopesar estos dos costos, es costoso en tiempo de mantenimiento no usar el enfoque try {} finally {} . Si bien la gente puede dicker sobre cuántos milisegundos fraccionados o instrucciones jvm adicionales se compara el bloque try {} finally {} con la otra versión, también se deben considerar las horas dedicadas a depurar la manera menos adecuada de abordar la desasignación de recursos.

Escriba el código que se puede mantener primero, y preferiblemente de una manera que evite que los errores se escriban más tarde.

    
respondido por el user40980 04.09.2013 - 16:30
5

enlace

La respuesta aceptada en esta pregunta muestra que el ajuste de una llamada de función en un bloque de captura de prueba cuesta menos del 5% en una llamada de función simple. En realidad, lanzar y atrapar la excepción hizo que el tiempo de ejecución se disparara a más de 66 veces la llamada de función. Por lo tanto, si espera que el diseño de la excepción se lance con regularidad, entonces trataría de evitarlo (en un código crítico de desempeño adecuadamente perfilado), sin embargo, si la situación excepcional es rara, entonces no es un gran problema.

    
respondido por el stonemetal 04.09.2013 - 15:49
4

En lugar de optimizar, piensa en lo que está haciendo tu código.

Agregar el bloque finally es una implementación diferente: significa que cerrarás sesión en cada excepción.

Específicamente: si el inicio de sesión genera una excepción, el comportamiento en su segunda función será diferente al primero.

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 la otra bien:

    void func()
    {
            bool ret = dosomething();
            if(ret == false)
                return;

            ret = dosomethingelse();
            if(ret == false)
                return;

            dootherstuff();

    }

y debería estar en una función que ya esté encapsulada en un contexto que administre el inicio / cierre de sesión, ya que parecen ser fundamentalmente diferentes de lo que está haciendo su código principal.

Tu 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:

    using(var loginContext = CreateLoginContext()) 
    {
            // Do all your stuff
    }

Y el contexto de inicio de sesión tiene un método Dispose que cerrará la sesión y limpiará los recursos.

Edit: ¡Realmente no contesté la pregunta!

No tengo pruebas medibles, pero no espero que esto sea más costoso o ciertamente no lo suficientemente costoso como para que merezca una optimización prematura.

Voy a dejar el resto de mi respuesta porque, aunque no respondo a la pregunta, creo que es útil para el OP.

    
respondido por el Michael 04.09.2013 - 15:20
1

Supuesto: estás desarrollando en código C #.

La respuesta rápida es que no hay un impacto significativo en el rendimiento al utilizar los bloques try / finally. Hay un impacto de rendimiento cuando se lanzan y atrapan excepciones.

La respuesta más larga es ver por ti mismo. Si observa el código CIL subyacente generado ( p. Ej., El reflector de puerta roja , entonces podrá ver las instrucciones CIL subyacentes generadas y ver el impacto de esa manera.

    
respondido por el Michael Shaw 04.09.2013 - 15:15
-2

Como ya se ha indicado en otros, el código Ttese tendrá resultados diferentes, si dosomethingelse o dootherstuff lanzan una excepción.

Y sí, cualquier llamada a función puede lanzar una excepción, ¡al menos un StackOVerflowError ! Si bien es raramente posible manejarlos correctamente (como cualquier función de limpieza llamada probablemente tenga el mismo problema, en algunos casos (como la implementación de bloqueos) es crucial incluso para manejar eso correctamente ...

    
respondido por el Steffen Heil 17.12.2014 - 09:07

Lea otras preguntas en las etiquetas