Rendimiento y optimización

Este documento proporciona una visión general de técnicas y herramientas que pueden ayudar a hacer que tu código Django se ejecute más eficientemente - más rápido, y utilizando menos recursos del sistema.

Introduction

Generalmente, la primera preocupación es escribir código que funcione, cuya lógica funcione como se requiere para producir el resultado esperado. A veces, sin embargo, esto no será suficiente para hacer que el código funcione de manera tan eficiente como uno desearía.

En este caso, lo que se necesita es algo - y en la práctica, a menudo una colección de cosas - para mejorar el rendimiento del código sin afectar su comportamiento, o solo mínimamente.

Enfoques generales

¿Qué estás optimizando?

Es importante tener una idea clara de qué se entiende por «rendimiento». No existe solo un métrica para medirlo.

Una mejora en la velocidad puede ser el objetivo más obvio para un programa, pero a veces se buscan otras mejoras en el rendimiento, como una menor consumo de memoria o menos demandas sobre la base de datos o la red.

Las mejoras en una área suelen traer consigo mejoras en otra, pero no siempre; a veces incluso puede ser al precio de otra. Por ejemplo, una mejora en la velocidad de un programa podría hacer que utilice más memoria. Incluso peor, puede ser autodestructivo - si la mejora en la velocidad es tan hambrienta de memoria que el sistema comienza a escasear de memoria, habrás hecho más daño que bien.

Hay otros equilibrios que tener en cuenta. Tu propio tiempo es un recurso valioso, más precioso que el tiempo del procesador. Algunas mejoras pueden ser demasiado difíciles para merecer la pena implementarlas o pueden afectar a la portabilidad o mantenibilidad del código. No todas las mejoras en el rendimiento son dignas de esfuerzo.

Por lo tanto, necesitas saber qué mejoras en el rendimiento estás buscando y también necesitas saber que tienes una buena razón para buscar en esa dirección - y para eso necesitas:

Medición del rendimiento

No es útil solo adivinar o suponer dónde se encuentran las ineficiencias en tu código.

Herramientas de Django

django-debug-toolbar es una herramienta muy útil que proporciona información sobre qué está haciendo tu código y cuánto tiempo le lleva hacerlo. En particular, puede mostrarle a usted todas las consultas SQL que su página está generando, y cuánto tiempo cada una ha tardado.

También están disponibles paneles de terceros para la herramienta de depuración, que pueden (por ejemplo) informar sobre el rendimiento del caché y los tiempos de renderizado de plantillas.

Servicios de terceros

Hay un número de servicios gratuitos que analizarán y reportarán sobre el rendimiento de las páginas de su sitio desde la perspectiva de un cliente HTTP remoto, en efecto simulando la experiencia de un usuario real.

Estos no pueden informar sobre los detalles internos de tu código, pero pueden proporcionar una visión útil del rendimiento general de tu sitio, incluyendo aspectos que no se pueden medir adecuadamente desde el entorno Django.

También hay varios servicios a pago que realizan un análisis similar, incluidos algunos que son conscientes de Django y pueden integrarse con tu base de código para perfillear su rendimiento de manera mucho más exhaustiva.

Haz las cosas bien desde el principio

Alguno del trabajo de optimización implica abordar las deficiencias en el rendimiento, pero parte del trabajo puede ser incorporado a lo que harías de todos modos, como parte de las buenas prácticas que debes adoptar incluso antes de empezar a pensar en mejorar el rendimiento.

En este sentido, Python es un lenguaje excelente con el que trabajar, porque las soluciones que parecen elegantes y se sienten bien suelen ser las mejores. Al igual que la mayoría de habilidades, aprender qué «se siente bien» requiere práctica, pero una de las guías más útiles es:

Trabaja a nivel adecuado

Django ofrece muchas formas diferentes de abordar las cosas, pero solo porque es posible hacer algo de una manera determinada no significa que sea la forma más adecuada de hacerlo. Por ejemplo, podrías encontrar que puedes calcular lo mismo - el número de elementos en una colección, tal vez - en un QuerySet, en Python o en un template.

Sin embargo, casi siempre será más rápido realizar este trabajo a niveles inferiores que a niveles superiores. A niveles superiores el sistema tiene que tratar con objetos a través de múltiples niveles de abstracción y capas de maquinaria.

Es decir, la base de datos puede hacer cosas más rápido que Python, que a su vez las hace más rápidas que el lenguaje del template:

# QuerySet operation on the database
# fast, because that's what databases are good at
my_bicycles.count()

# counting Python objects
# slower, because it requires a database query anyway, and processing
# of the Python objects
len(my_bicycles)
<!--
Django template filter
slower still, because it will have to count them in Python anyway,
and because of template language overheads
-->
{{ my_bicycles|length }}

Hablando en general, el nivel más apropiado para el trabajo es el de menor nivel que sea cómodo escribir.

Nota

El ejemplo anterior es meramente ilustrativo.

En primer lugar, en un caso real debes considerar lo que está sucediendo antes y después de tu conteo para determinar qué forma óptima hay de hacerlo en ese contexto particular. El documento sobre optimización de la base de datos describe un caso donde contar en el template sería mejor.

En segundo lugar, hay otras opciones que considerar: en un caso real, {{ my_bicycles.count }}, que invoca directamente al método count() del conjunto de consultas desde el template, podría ser la elección más adecuada.

Caché

A menudo es costoso (es decir, hambriento de recursos y lento) computar un valor, por lo que puede haber una gran ventaja en guardar el valor en una caché accesible rápidamente, lista para cuando se requiera la próxima vez.

Es una técnica suficientemente significativa y poderosa como para que Django incluya un marco de trabajo de caching integral, así como otras piezas más pequeñas de funcionalidad de caching.

El marco de trabajo de caché

El marco de Django para la caché < > ofrece oportunidades muy significativas de ganancia en rendimiento, al guardar contenido dinámico de modo que no necesite ser calculado para cada solicitud.

Para conveniencia, Django ofrece diferentes niveles de granularidad en caché: puedes cachear el resultado de vistas específicas, o solo las piezas que son difíciles de producir, o incluso un sitio completo.

La implementación de caché no debe considerarse como una alternativa para mejorar el código que se ejecuta mal porque ha sido escrito mal. Es uno de los últimos pasos hacia la producción de código bien ejecutado, y no un atajo.

propiedad_cachada

Es común tener que llamar al método de una instancia de clase más de una vez. Si esa función es costosa, entonces hacerlo puede ser derrochador.

Usando el decorador cached_property se almacena el valor devuelto por una propiedad; la próxima vez que se llama a esa función en ese objeto, devolverá el valor almacenado en lugar de re-calcularlo. Ten en cuenta que esto solo funciona con métodos que toman self como único argumento y que cambia el método a una propiedad.

Algunos componentes de Django también tienen su propia funcionalidad de caché; se discuten a continuación en las secciones relacionadas con esos componentes.

La comprensión de la pereza

Relajación es una estrategia complementaria al caché. El caché evita la recomputación guardando los resultados; la relajación pospone la computación hasta que se requiere realmente.

La pereza nos permite hacer referencia a cosas antes de que sean instanciadas, o incluso antes de que sea posible instanciarlas. Esto tiene numerosos usos.

La traducción de los textos es la siguiente:

La pereza también es una forma de ahorrar esfuerzo intentando evitar el trabajo desde el principio. Es decir, uno de los aspectos de la pereza es no hacer nada hasta que tenga que ser hecho, porque puede no resultar necesario al final. La pereza puede tener implicaciones en cuanto a rendimiento, y más aún si el trabajo concerniente es costoso.

Python proporciona una serie de herramientas para la evaluación perezosa, particularmente a través de los constructos generador y expresión generadora. Es recomendable leer sobre la pereza en Python para descubrir oportunidades para aprovechar patrones perezosos en tu código.

Pereza en Django

Django es bastante perezoso por sí mismo. Un buen ejemplo de esto se puede encontrar en la evaluación de un QuerySet. Los QuerySets son perezosos. Por lo tanto, un QuerySet se puede crear, pasar y combinar con otros instancias de QuerySet, sin incurrir en viajes innecesarios a la base de datos para obtener los elementos que describe. Lo que se pasa es el objeto QuerySet, no la colección de elementos que - eventualmente - serán requeridos de la base de datos.

Por otro lado, ciertas operaciones forzarán la evaluación de un QuerySet. Evitar la evaluación prematura de un QuerySet puede ahorrar hacer una expedición costosa y innecesaria a la base de datos.

Django también ofrece el decorador keep_lazy() . Esto permite que una función que ha sido llamada con un argumento perezoso se comporte perezosamente, solo siendo evaluado cuando sea necesario. Por lo tanto, el argumento perezoso - que podría ser uno costoso - no será llamado para la evaluación hasta que sea estrictamente requerido.

Bases de datos

Optimización de bases de datos

La capa de base de datos de Django proporciona varias formas de ayudar a los desarrolladores a obtener el mejor rendimiento posible de sus bases de datos. La documentación de optimización de bases de datos reúne enlaces a la documentación relevante y ofrece consejos adicionales que resumen los pasos a seguir cuando se intenta optimizar el uso de la base de datos.

Rendimiento HTTP

Middleware

Django viene con algunas piezas útiles de middleware que pueden ayudar a optimizar el rendimiento del sitio web. Incluyen:

ConditionalGetMiddleware

Agrega soporte para navegadores modernos para obtener respuestas condicionales basadas en los encabezados ETag y Last-Modified. También calcula y establece un ETag si es necesario.

GZipMiddleware

Comprime las respuestas para todos los navegadores modernos, ahorrando ancho de banda y tiempo de transferencia. Ten en cuenta que GZipMiddleware se considera actualmente un riesgo de seguridad y es vulnerable a ataques que anulan la protección proporcionada por TLS/SSL. Consulte el aviso en GZipMiddleware para obtener más información.

Sesiones

Usando sesiones respaldadas por caché

Uso de sesiones cacheadas puede ser una forma de aumentar el rendimiento eliminando la necesidad de cargar datos de sesión desde un almacenamiento más lento como la base de datos y en su lugar almacenar frecuentemente usados datos de sesión en memoria.

Archivos estáticos

Los archivos estáticos, que por definición no son dinámicos, hacen una excelente meta para obtener ganancias de optimización.

ManifestStaticFilesStorage

Al aprovechar las capacidades de caché de los navegadores web, puedes eliminar completamente los accesos a la red para un archivo dado después del primer descarga.

ManifestStaticFilesStorage agrega una etiqueta dependiente del contenido a los nombres de archivo de archivos estáticos para hacer seguro que los navegadores puedan cachearlos a largo plazo sin perder cambios futuras - cuando un archivo cambia, también cambiará la etiqueta, por lo que los navegadores recargarán automáticamente el activo.

«Minificación»

Varios herramientas y paquetes de terceros de Django proporcionan la capacidad de «minificar» HTML, CSS y JavaScript. Ellos eliminan espacios en blanco innecesarios, saltos de línea y comentarios, y acortan nombres de variables, y así reducen el tamaño de los documentos que tu sitio publica.

Rendimiento de plantillas

Nota que:

  • Usando {% block %} es más rápido que usar {% include %}

  • Los plantillas muy fragmentadas, ensambladas de muchas pequeñas piezas, pueden afectar el rendimiento

El cargador de plantillas cacheado

Habilitar el cargador de plantillas cacheado a menudo mejora el rendimiento drásticamente, ya que evita compilar cada plantilla cada vez que necesita ser renderizada.

Usando diferentes versiones del software disponible

A veces puede valer la pena comprobar si existen versiones diferentes y mejoras en cuanto a rendimiento del software que estás utilizando.

Estas técnicas están dirigidas a usuarios más avanzados que quieren empujar los límites del rendimiento de un sitio Django ya bien optimizado.

Sin embargo, no son soluciones mágicas para problemas de rendimiento y es poco probable que brinden ganancias mejor que marginales en sitios que no hagan las cosas básicas correctamente.

Nota

Es importante repetir: buscar alternativas al software que ya estás utilizando nunca es la primera respuesta a los problemas de rendimiento. Cuando llegas a este nivel de optimización, necesitas una solución formal de medición de rendimiento.

Lo nuevo a menudo - pero no siempre - es mejor

Es bastante raro que una nueva versión de un software bien mantenido sea menos eficiente, pero los mantenedores no pueden anticipar cada posible caso de uso - por lo tanto, si es consciente de que las versiones más recientes suelen ser más rápidas, no asuma que siempre será así.

Esto es cierto también para Django mismo. Las sucesivas versiones han ofrecido una serie de mejoras a lo largo del sistema, pero aún así debes comprobar el rendimiento real de tu aplicación, porque en algunos casos podrías encontrar que los cambios significan que se comporta peor en lugar de mejor.

Las nuevas versiones de Python y también de paquetes de Python suelen ser más rápidas a menudo - pero mide, en lugar de asumir.

Nota

A menos que hayas encontrado un problema de rendimiento inusual en una versión particular, generalmente encontrarás mejores características, confiabilidad y seguridad en una nueva versión y que estos beneficios son mucho más significativos que cualquier mejora o pérdida de rendimiento.

Alternativas al lenguaje de plantillas de Django

Para casi todos los casos, el lenguaje de plantillas incorporado de Django es perfectamente adecuado. Sin embargo, si los puntos débiles en tu proyecto de Django parecen estar en el sistema de plantillas y has agotado otras oportunidades para remediar esto, una tercera parte alternativa puede ser la respuesta.

Jinja2 puede ofrecer mejoras del rendimiento, particularmente cuando se trata de velocidad.

Los sistemas de plantillas alternativos varían en la medida en que comparten el lenguaje de plantillas de Django.

Nota

Si experimentas problemas de rendimiento en las plantillas, lo primero que debes hacer es entender exactamente por qué. Utilizar un sistema de plantillas alternativo puede resultar más rápido, pero los mismos beneficios pueden estar disponibles sin ese trabajo - por ejemplo, el procesamiento y la lógica caros en tus plantillas podrían realizarse con mayor eficiencia en tus vistas.

Implementaciones de software alternativas

Es posible que valga la pena comprobar si el software de Python que estás utilizando ha sido proporcionado mediante una implementación diferente que pueda ejecutar el mismo código más rápido.

Sin embargo: la mayoría de los problemas de rendimiento en sitios bien escritos con Django no están a nivel de ejecución del código Python, sino más bien en consultas de bases de datos ineficientes, caché y plantillas. Si estás confiando en un código Python mal escrito, es poco probable que tus problemas de rendimiento se resuelvan ejecutándolo más rápido.

Usar una implementación alternativa puede introducir problemas de compatibilidad, despliegue, portabilidad o mantenimiento. No hay que decir que antes de adoptar una implementación no estándar debes asegurarte de que proporciona ganancias suficientes en rendimiento para que tus aplicaciones superen los riesgos potenciales.

Con estas precauciones en mente, debes estar al tanto de:

PyPy

PyPy es una implementación de Python en Python mismo (la implementación estándar de Python está en C). PyPy puede ofrecer ganancias significativas en rendimiento, típicamente para aplicaciones pesadas.

Un objetivo clave del proyecto PyPy es la compatibilidad con las API y bibliotecas de Python existentes. Django es compatible, pero necesitarás verificar la compatibilidad de otras bibliotecas en las que confíes.

Implementaciones de C de bibliotecas de Python

Algunas bibliotecas de Python también están implementadas en C y pueden ser mucho más rápidas. Buscan ofrecer las mismas API. Ten en cuenta que no son desconocidas las compatibilidad problemas y diferencias de comportamiento (y no siempre es inmediatamente evidente).