Freigeben über


Por qué los bloques Try-Catch sí afectan el performance

He escrito un post en el cual hablo de las Buenas Prácticas en Manejo de Excepciones .Net. Tuve un comentario muy interesante en el cual la persona me mencionaba que contrario a lo que yo afirmé en ese post, los bloques try..catch no generaban mayor impacto en el performance de la aplicación. Y me citó algunos artículos al respecto.

Aunque los artículos obedecen a pruebas que en ocasiones producen casi los mismos resultados usando o no excepciones, puedo decirles muy respetuosamente que son bastante ingenuos porque no tienen en cuenta la estructura y funcionamiento del framework.net y sobre todo del optimizador de compilación.

Así pues, les comento que para hacer la afirmación que hice, no ejecuté ninguna prueba. Solo hablé de una conclusión bastante lógica que se tiene al estudiar el comportamiento del optimizador de compilación del framework:

Tomemos como ejemplo el siguiente código escuelero:

    1:      int cuenta = 1;
    2:      AlgunMetodo();
    3:      cuenta++;
    4:      OtroMetodo();
    5:      cuenta++;
    6:      Console.WriteLine(cuenta); 

Cuando esto es compilado, el JIT de .NET efectivamente optimiza todo a:

    1:  AlgunMetodo();
    2:  OtroMetodo();
    3:  Console.WriteLine(3);

Que obviamente corre enormemente más rápido.

Sin embargo, para poder mantener las reglas de ordenamiento de memoria distribuida a través de hilos durante el tiempo de ejecución y compilación el CLR deshabilita TODA OPTIMIZACION que afecte el orden de lecturas y escrituras del CIL en los bloques protegidos. Los bloques protegidos conocidos también como regiones protegidas, bloques resguardados o bloques “try”.

Esto en castellano sencillo nos indica que el JIT no puede optimizar código metido en bloques try..catch. Es por esto que recomiendo reducir el uso de excepciones al mínimo necesario. Y usarlas solo para solucionar problemas no relativos a negocio, que se pueden solventar simplemente con un algoritmo más inteligente.

También por esto menciono que esos artículos son ingenuos, porque para hacer una prueba fehaciente deberíamos incluir un algoritmo de uso común en el cuál la optimización sí marca la diferencia.

Así que si usamos el try-catch tendríamos:

    1:      int cuenta= 1;
    2:      try
    3:      {
    4:          AlgunMetodo();
    5:          cuenta++;
    6:          OtroMetodo();
    7:          cuenta++;
    8:      }
    9:      finally
   10:      {
   11:          Console.WriteLine(cuenta);
   12:      } 

En este caso, al carecer de optimizaciones, la variable inicia con el valor de 1, luego se ejecuta AlgunMetodo(), luego la variable cuenta se vuelve a ajustar, luego OtroMetodo(), luego una tercera actualización y finalmente se escribe el calor de una variable, que cuesta más en ciclos de procesador, que escribir una constante. En síntesis, estamos desperdiciando ciclos.

Estas optimizaciones que mostré aquí son de las mas sencillas que hace el JIT; pero no son las únicas; existen muchas otras optimizaciones que se pierden dentro de los bloques try..catch. Súmenle a esto le hecho de que efectivamente estas instrucciones consumen más memoria (poca o mucha pero la consumen) y as’;i podrán concluir por qué es mejor usarlas muy prudentemente.

Comments

  • Anonymous
    September 08, 2010
    GRacias!! muy interesante saberlo.

  • Anonymous
    September 08, 2010
    Excelente, muchas gracias por tu aclaracion

  • Anonymous
    September 09, 2010
    Agregar que las pruebas de rendimiento se deben hacer con el codigo compilado en "Release"

  • Anonymous
    September 09, 2010
    Walter. Esta de pelos la explicación. Ahora las dudas: En una app de tres capas normales: datos, negocio y GUI. Las excepciones deberían manejarse principalmente en Datos y GUI? las de negocio como usted indica deberían ser controladas con buenas reglas de negocio (buen análisis funcional)? Nosotros Usamos un logger de excepciones que crea un log con wraper de excepciones hecho por nosotros. Eso evita tener que propagar las excepciones. QUe tan bueno es eso? gracias compañero

  • Anonymous
    September 10, 2010
    Este tipo de explicaciones hacen que todo sea mas fácil de entender. Muchas gracias. Saludos.

  • Anonymous
    September 10, 2010
    @Ricker: "En una app de tres capas normales: datos, negocio y GUI. Las excepciones deberían manejarse principalmente en Datos y GUI?" Sí... esto tiene mucho sentido, dado que son las capas más propensas a recibir entradas o comportamientos incontrolables por la lógica del negocio. Como por ejemplo que el usuario intente cargar un archivo corrupto o que la base de datos esté caída y no acepte queries. " las de negocio como usted indica deberían ser controladas con buenas reglas de negocio (buen análisis funcional)?" Más que un buen análisis funcional (que obviamente es imperativo), se requiere un buen despliegue algorítmico. De hecho, es aquí donde se destacan los buenos programadores de los que solo programan. Ellos son capaces de predecir todos los posibles puntos de quiebre de un algoritmo y tratar de manejarlos a punta de lógica hasta el máximo posible. Sin embargo hemos de tener en cuenta que determinadas situaciones no dejan más posibilidad que generar una excepción por lo cual es quí en este único punto donde las manejaríamos con el esquema try..catch. Es cuestión de balance. Muchas gracias por preguntar!

  • Anonymous
    September 10, 2010
    @Noe: Muchas gracias, es con mucho gusto. Por aquí quedo pendiente!