Tuesday, July 29, 2008

LINQ - Cuestiones de performance

Bien, sinceramente quisiera comentarles todo lo que he podido averiguar, investigar, estudiar y conversar al respecto.
La verdad es que el post sería interminable y como le decia a David, pues, no tendría mucho sentido (explicaciones sobran)

De LINQ puedo decirles que hace tiempo que nos enteramos de su advenimiento, yo por mi parte, estaba emocionado, recuerdo que alguna vez dije frameworks sobre frameworks, y lo mejor, frameworks y mas frameworks!

Es que, al fin y al cabo, todo lo que competa a Framework 3.0 / 3.5 y relacionados, significa para mi frameworks sobre frameworks.
Siendo la base, pues nuestro querido .net framework (2.0, por supuesto).

Es cierto, Linq2Sql es muy interesante, que uno no necesite conocer T-SQL para trabajar y realizar ejecuciones contra la base de datos, puede ser para muchos, un gran alivio.

No para mi.

Considero que si somos algo puristas al respecto, uno debe realizar una tarea con la mejor herramienta disponible, a su vez, debe contarse con el experto en el ambito.
No recuerdo cuando fue la ultima vez que estuve en un proyecto en el que no habia DBA, la verdad no recuerdo, quizá fue en la universidad, pero hace mucho tiempo que ademas de un DBA, el personal de desarrollo tiene la capacidad necesaría para construir un stored procedure con menor probabilidad de ajuste en lo que respecta a lógica y performance.

Por ese aspecto no me preocuparía mucho tener como alternativa, el uso de Linq2Sql.

Pero dejemos mi punto de vista de lado, comencemos con una de mis preocupaciones.
Creo yo, con el tiempo he mencionado algunas de ellas, pero una vez no está de mas.

Estaba algo interesado en la forma de trabajo de LINQ, pues es interesante todo esto de los datacontext y expresiones de consulta dentro del código .net, pero queria ver exactamente como hacía para obtener la información de la base de datos.

Encontré la respuesta con un poco de código a la mano y una que otra depuración cuando llegaba a la expresion de consulta.
Ahora es menos complicado encontrarnos con imagenes como la siguiente (via lancefisher.net):


Como puede notarse, la expresión se transformará en T-SQL.

Todos felices, no? el CLR se encarga de interpretar parte de lo que escribimos, transformándolo en una consulta SQL que será enviada a ejecutarse contra la base de datos.

Dije, interpretar. Es decir, estariamos regresando, en parte al código interpretado.

Por otro lado, el T-SQL generado será enviado desde nuestra aplicación hacia la base de datos, cierto?
Si lo tomamos desde otro punto de vista, estamos regresando al concepto de escribir las sentencias SQL dentro de la aplicación.
Estariamos obviando entonces el uso de stored procedures?
Claro, este es un tema controversial, como puede notarse aquí, aquí, aquí o aquí.
Por mi parte soy partidario del uso de stored procedures.

Cuando hablamos de performance uno recuerda al instante "tiempo de respuesta al cliente", lo cual es cierto, pero que pasa si hablamos de espacios de memoria? y qué hacemos con la memoria disponible?
Claro, a estas alturas uno puede olvidar esos "aspectos mínimos", aqui me estoy refiriendo al hardware.
Lamentablemente ya se han obviado las epocas en las que se luchaba por cada bit a usarse, quizá ese sea el problema.

Pero si, "esas cosas" influyen y bastante, solo para darnos una idea, veremos algunos escenarios. Para esto usaremos Northwind.

Tiempos de respuesta: Linq en C# - Linq en VB
Lo que se hizo fue construir una expresion que contiene un filtro simple, no mostraré la imagen depurando la aplicación. Pero puedo adelantarles que la expresión SQL de la imagen anterior solo puede reproducirse cuando se trabaja en una aplicación basada en C#.

Aquí la consulta construida.

Expresiones

Para seguir con el ejemplo usaremos las herramientas recomendadas por Carlos Walzer (esto en su sección Anti Prácticas.NET, la cual es una lectura que no deben dejar pasar), en este caso usaremos Jet Brains luego el CLR Profiler.

Usando JetBrains para el Ejemplo en VB
TiemposVB

Usando JetBrains para el Ejemplo en CS
TiemposCS

La linea resaltada indica el tiempo de respuesta para la ejecucion del ejemplo mostrado.
La diferencia es notoria, no? bueno, algo tendrán que ver los chicos de C# no? (es que, Linq fue creado sobre C#)

Para el siguiente caso se trabajará sobre el ejemplo en C#.


Tiempos de respuesta: Linq en C# - Ejecución de T-SQL desde código C#

Se agregó el siguiente código de prueba.

SqlEnCodigo

En este caso no se está usando una librería en particular, bueno, continuemos con las pruebas de tiempos de respuesta.
TiemposCsSql

Como puede observarse, ejecutar el código mostrado, demora 2688 ms, el cual, comparado con los 2521 ms del caso Linq en C#, nos indica que usar Linq en C# es mas rápido.

Debemos confiar en este resultado?
Estamos hablando de poco mas de 100 milisegundos.
Por esta diferencia debemos cambiar de forma de trabajo?
Pues bien, que ganamos? Un modelo completamente tipado (recordemos que las primeras propuestas venian de la mano con los datasets tipados).
Esa es una buena respuesta, pero aun no me siento convencido.


Hablemos de memoria: Linq en C# - Ejecución de T-SQL desde código C#
Aquí usaremos el CLR Profiler, la verdad es que me gusta bastante esta herramienta, sirve para darnos una vista diferente cuando hablamos de performance.

Usando CLR para ejemplo Linq
MemoriaLinq

Usando CLR para ejemplo C#
MemoriaCs

Aqui el asunto es algo preocupante.
Como puede observarse, el uso de memoria nos muestra un 607kB de Linq contra 187kB de C# tradicional, es decir estamos hablando de mas de 100% de diferencia en lo que respecta a consumo de recursos.
Qué deberiamos hacer al respecto? Qué modelo deberiamos seguir?
Yo aun no me siento convencido.

Consideraciones
- En primer lugar, el ejemplo usando DataSet es algo que no debería ocurrir en el mundo real, a pesar de que he visto lugares en los que se han construido asi, y que, funcionan!, asi que, por qué cambiarlo? Es lo que dirian, estoy seguro que si. Recuerdan el post del paradigma?
- Como les iba diciendo, el ejemplo fue con un DataSet, y hablando seriamente, la diferencia se volvería abismal si es que realizamos el ciclo completo usando un DataReader. Aqui aplicaremos la lógica y los mitos cazados por Carlos Walzer en su seccion Antiprácticas .Net. (Oye Carlos, no me conoces, pero ya te estoy haciendo bastante propaganda!!!!, es broma, es broma)
- Debemos tomar en cuenta que dejé el ejemplo con la sentencia SQL incrustada en el código C#, la respuesta mejorará si es que hablamos de una llamada a un stored procedure.
- Aplicando lo aprendido por los posts de Carlos, comprenderemos a detalle que el DataReader combinado con modelos de entidades y listas genéricas, es de momento, la mejor alternativa en lo que respecta al trabajo y desarrollo orientados al modelo de base de datos.
- El modelo tipado ofrecido por Linq (Aqui me refiero a los datacontext y entidades análogas a las tablas de la BD) puede ser reemplazado por herramientas que generan entidadas mapeadas a las tablas (Aqui me estoy refiriendo a generadores de código, en el mercado hay cientos, y que no decir en cada empresa de desarrollo)
- Este post no busca controversia alguna, hace mucho que conocí una frase que nos saca de algunas situaciones y esta es "eso depende" (y esto a veces genera desorden)
- Personalmente hablando, considero Linq como un lenguaje interesante, pero que debe afinarse en muchas cosas.
- Lo bueno de todo esto es que uno puede seguir construyendo sus procedimientos SQL y enlazarlos a las clases Linq2Sql, pero eso es mas trabajo, no? (Qué creian que el designer nos iba a ayudar toda la vida?)
- Me despido

Saludos[at]Cama

2 comments:

NOMBRE said...

interesante, veremos que dice el tiempo

Anonymous said...

Claro!
Lo mas probable es que en las proximas versiones hayan bastantes mejoras.