EF 4.0 Performance Tips #5 (#1 Revisited )

El el primer tip de rendimiento sobre EF 4.0, #1, hablamos sobre las diferencias que se producían si en nuestras consultas sobre L2E hardcodeábamos valores o hacíamos uso de variables. Básicamente mientras el uso de variables provocaba que las consultas fueran parametrizadas el hecho de establecer valores directamente  hacía que nuestras consultas fueran adhoc y por lo tanto un trashing de nuestra cache de planes de ejecución.

Este comportamiento es lo normal y la recomendación es nunca hardcodear valores, aunque hay cierto tipo de consultas que no se comportan como quizás deberían y aunque sigamos las recomendaciones podremos observar alguna consulta AdHoc. La que yo conozco, eso no quita que pueda existir algún caso más tiene que ver con el uso de los métodos extensores SKIP y TAKE de L2E, los cuales por alguna razón, que la sé y no me convence para nada, ‘funcletizan’ los valores y eliminan la posiblidad de hacer las consultas parametrizadas.

 

Vamos a la práctica y pongamos las siguientes lineas de código:

 

 

Ámbos métodos obtienen una colección de Customer de forma paginada y hacen uso de variables para indicar los parámetros de paginación. Según lo expuesto por mi en el tip #1 la consulta en L2E debería de ser parametrizada ¿verdad?. Pues veamos los resultados haciendo una consulta sobre la vista administrada que nos da los elementos almacenados en la cache de planes de ejecución de Sql Server.

 

Untitled

Como se puede observar, la fila 2 que se corresponde en este caso a la consulta en ESQL ha parametrizado la misma, mientras que la consulta 3, L2E, es una consulta AdHoc.

 

Bien, ya hemos expuesto el problema, ahora toca hablar de las soluciones. Lógicamente ya hemos expuesto una de ellas, sustituir las consultas que usan paginación escritas en L2E por consultas que usen ESQL, dentro de poco trataré de enseñar como construir un método extensor que nos ayude en esta tarea. Otra de las posibles soluciones es tratar de “confiar” en el mecanismo de auto parametrización de Sql Server, aunque esto no se si sería demasiado realista.  Para terminar, el tercer workaround, para intentar solventar este problema sería hacer uso de CompiledQuery tal y como vemos a continuación.

 

 

Si examinamos ahora los planes de ejecución veremos como ahora, ambas consultas son parametrizadas.

Untitled2

Saludos

Unai

EF 4.0: Método Include sin hardcodear las propiedades de navegación

Tal y como comentaba en el último Tip de rendimiento de EF, aquí, el uso del método de expansión de consultas de ADO.NET EF conocido como Include tiene la pega de que es necesario entrecomillar los distintos path de las navegaciones. Como en cualquier otro elemento, el problema básico de poner entre comillas un elemento, es que el compilador no será capaz de advertirnos de esos elementos puesto que para el cualquier cosa será correcta. Imagínese que en el código de sus consultas utiliza este método de expansión de consultas y que posteriormente decide cambiar/eliminar alguno de los nombres de las propiedades de navegación, entonces el código compilará pero en ejecución fallara si estos path de navegación son incorrectos.

Una forma de resolver este problema es por medio de la creación de un nuevo método extensor en el que en vez de escribir un string con el path de navegación podamos establecer un arbol de expresiones que sea capaz de indicarnos realmente el camino de la expansión de consultas. En vez de partir de cero, el tiempo es oro, pondré el mismo trozo de código que he utilizado para la iniciativa de arquitectura de Microsoft, que conocemos con NLayer y que está alojada en Codeplex.

 

 

Gracias a este método extensor, si se requiere hacer una expansión de consultas podríamos escribir algo como lo que sigue:

Por lo tanto, si se renombran o se eliminan las propiedades de navegación, dentro de un modelo EDM el compilador podrá detectar errores al evaluar las expressiones como c=>c.CustomerPicture.

 

NOTA IMPORTANTE 1:  Cómo se ha comentado en este mismo post esta implementación esta contenida dentro de http://microsoftnlayerapp.codeplex.com y tiene algun elemento que depende de la misma como InMemoryObjectSet, revise este código.    

 

NOTE IMPORTANTE 2:  Esta forma de implementar el método extensor de Include tiene una limitación importante que consiste en que solamente se pueden agregar más de un path de navegación si se hace por medio de relaciones 1..0|1.                

 

Saludos

Unai Zorrilla

EF 4.0 Performance Tips #4

Como muchos sabréis, sobre todo lo que ya habéis jugado con la primera version de ADO.NET Entity Framework, una de las novedades en la version actual es la carga perezosa de forma implícita o automática. Esta nueva opción de carga viene marcada de forma automática con la plantilla de generacion de código por defecto, clases prescriptivas, por medio de una opción llamada LazyLoadingEnabled tal como podéis observar en el siguiente trozo de código que muestra el constructor por defecto de un contexto de trabajo generado con ADO.NET EF.

Aunque esta opción es realmente cómoda, ya que nos libra dentro del ambiente de un contexto de tener que hacer un Load explícito de una propiedad de navegación, si no tenemos cuidado puede resultar un punto negro dentro del rendimiento por un incremento innecesario de consultas a la base de datos. Pongamos un ejemplo típico de una relación uno a muchos como la que se muestra en la siguiente figura.

 

Untitled

Ahora, partiendo de este modelo imáginese que se realiza la siguiente operación:

 

Si miramos el número de consultas por medio de cualquier profiler o Intellitrace podremos ver como el número de consultas es N+1 siendo N el número de elementos de tipo Detail contenidos dentro de la base de datos.

 

Untitled2

Lógicamente, si conocemos que dentro de nuestro negocio necesitamos acceder a esos datos una solución para resolver este problema es realizar una expansión de consultas, indicando a la consulta de detalles que vamos a necesitar acceder también a su navegación con los maestros. Para realizar esta expansión de consultas en ADO.NET EF disponemos desde la primera versión del producto del método Include. El siguiente trozo de código muestra como realizar el mismo proceso que anteriormente con expansión de consultas.

NOTA: Para todos los que estéis chillando por poner el nombre de la propiedad de navegación, Master, entre comillas, saber que es posible crear un método extensor para superar este problema, pondré un post con esto dentro de poco.

 

Si revisamos ahora nuestro Intellitrace veremos como el número de las consultas se ha quedado solamente en 1, que consiste en un simple INNER JOIN entre Detail y Master

 

Untitled3

 

Saludos

Unai Zorrilla

EF 4.0 Performance Tips #3

En los dos primeras entregas, #1 y #2 hemos visto algunos pequeños elementos que harán mejorar de una forma bastante importante nuestras aplicaciones usando ADO.NET EF, aunque para ello apenas hemos tenido que tocar nuestro código y/o nuestros modelos de entidades. En esta tercera entrega, veremos un ejemplo de una técnica de modelado de entidades que nos permitirá relajar ciertas consultas que pudieran representar un posible problema dentro de nuestras aplicaciones.

La técnica en cuestión se conoce como Table Spliting y básicamente consiste en poder separar los datos de una entidad, mapeada a una única tabla, en varias entidades con una relacion 1:1 entre las mismas. El primer ejemplo que vamos a ver , y uno de los en que mejor se puede ver la utilidad de esta técnica, consiste en el típico caso de una tabla con clientes en el que además se almacena la imagen del mismo, figura 1.

img1 

Si partimos de la base de datos para crear un modelo de entidades lógicamente ADO.NET EF creará por nosotros una entidad Customer que contega este campo Imagen como parte de la entidad. A partir de aquí cualquier consulta a esta entidad obtendrá de la base de datos este campo, a pesar de que en una aplicación normal las veces que realmente necesitemos el campo imagen sea infima con respecto al resto de consultas. Esto, lógicamente, puede tener un impacto fuerte, en primer lugar por el probable tráfico de datos que podemos transmitir por la red, si cada cliente tiene una imagen de 1MB y seleccionamos 1000 imágenes podemos mover a traves de la red 1000 MB presúntamente innecesarios.

 

Los pasos para realizar nuestro “Table Spliting” consiste en crear una nueva entidad, por ejemplo CustomerPicture, e incluir en esta el campo Imagen y el campo clave que nos permitirá realizar la asociacion entre esta entidad y la entidad Customer.

 

img2

Una vez creada la asociación, establecemos correctamente el mapeo de la entidad CustomerPicture para mapearla con la tabla “customers”.

img3

Como se puede observar en estos pasos a grosso modo, la técnica de table spliting es muy facil realizarla directamente en nuestros modelos y con la misma podemos obtener los resultados buscados.

 

Aunque en principio esta técnica le parezca que está restringida a estos casos tan particulares la realidad es que no es así. En muchas ocasiones, con modelos legacy, nos podemos encontrar con tablas con una gran cantidad de columnas. Las consultas a estas entidades, en EF como en cualquier otro framework ORM, siempre traen todos los campos de la tabla y esto podría provocar lentitud en las mismas si las tablas tienen bloqueos frecuentas, incluso aunque estas consultas tengan predicados con una correcta utilización de índices. Ante estos casos una posible solución es utilizar Table Spliting como acabamos de ver o bien realizar materializaciones parciales usando Tuple o tipos anónimos.

 

Saludos

Unai Zorrilla Castro

EF 4.0 Performance Tips #2

Uno de los elementos más fáciles de implementar y gracias al cual notaremos una importante mejora dentro de nuestras aplicaciones usando .NET Entity Framework, aquí es independiente la version, es el uso de la “precompilación de vistas”.

Uno de de los mayores puntos en la creación de la abstracción de la base de datos por parte de EF consiste en la creacion de vistas de consulta y actualización en el lenguaje nativo de la base de datos. Durante este proceso hay una carga importante de tiempo y de consumo de memoria, aunque las mismas se cachean dentro del dominio de aplicación en posteriores usos. Con el fin de ver cuanto de importante es este punto podéis echar un vistazo a la siguiente gráfica, donde podemos observar que este proceso representa aproximádamente un 56%, el color violeta, en un proceso de ejecución de una consulta.

untitled

Afortunadamente, para resolver este problema podemos precompilar e incluir en el proyecto estas vistas, de tal forma que nos libremos en tiempo de ejecución de su creación, por medio de la herrramienta EDMGEN.exe. Los pasos para realizarlas los tenéis disponibles aquí.

 

Otra de las opciones para pregenerar estas vistas es mediante la utilización de una simple plantilla T4, que realiza todo el trabajo anterior por nosotros y por lo tanto nos libramos de tener que incorporar opciones de precompilación a los proyectos que tengan modelos de Entity Framework.  La citada plantilla T4 os la podéis descargar de su enlace original en el blog del equipo de ADO.NET  aunque la misma no la han actualizado a la version final de EF 4.0 y por lo tanto NO OS FUNCIONARÁ CORRECTAMENTE, pero dejo aquí el enlace original. La plantilla retocada y adaptada a VS2010 RTM la podéis descargar desde este mismo post aquí, en realidad solamente hay que hacer un cambio de xml namespaces. Una vez descargada eliminar la extensión .txt, e incluirla en el mismo proyecto en el que tengais el archivo EDMX, renombrando la misma sustituyendo $ModelName$ por el nombre de vuestro EDMX.

 

Enlaces de interes

 

http://blogs.msdn.com/b/dmcat/archive/2010/04/21/isolating-performance-with-precompiled-pre-generated-views-in-the-entity-framework-4.aspx

http://blogs.msdn.com/b/adonet/archive/2008/06/20/how-to-use-a-t4-template-for-view-generation.aspx

http://msdn.microsoft.com/en-us/library/bb896240.aspx

http://blogs.msdn.com/b/adonet/archive/2008/02/04/exploring-the-performance-of-the-ado-net-entity-framework-part-1.aspx

 

Saludos

Unai

EF 4.0 Performance Tips #1

Ya desde hace algún tiempo me rondaba por la cabeza la idea de poner post con tips de rendimiento a tener en cuenta dentro de Entity Framework 4.0, algunos de ellos serán más habituales y sencillos de resolver, otros por contra puede que sean más complicados de encontrar. Espero que la carga de trabajo me ayude a que los siguientes números no se dilaten demasiado 🙂

 

Empezamos por una sencillita, ¿cual es la diferencia entre las siguientes consultas en Linq2Entities y que impacto podría tener en el rendimiento de las mismas?

A)

La respuesta a esta pregunta es sencilla, para la primera consulta, sin parametrizar, Entity Framework dispara una consulta AdHoc, ni siquiera DSE, con lo cual el plan de ejecución no se reaprovechará si esa misma consulta se realiza con valores de, en este caso, FirstName distintos al actual, forzando por lo tanto un movimiento muy grande de elementos en la cache de planes de ejecución y por lo tanto disminuyendo el rendimiento.

Para el caso de la consulta B, EF ejecuta esta consulta mediante sp_executesql ( Force Statment Caching ), gracias a lo cual esta consulta paremetrizada puede reutilizar el mismo plan de ejecución aun variando los valores de los parámetros.

 

Puede observar las diferencias de estos comportamientos ejecutando la siguiente consulta:

 

SELECT *  from sys.dm_exec_cached_plans cp cross apply sys.dm_exec_sql_text(plan_handle)

 

Untitled

 

 

Saludos

Unai