2009-07-23 18 views
5

Estaba comparando un antiguo script PHP versus la versión Django más nueva y lujosa y la versión PHP, con escupción completa de HTML y todo funcionaba más rápido. MUCHO más rápido hasta el punto de que algo tiene que estar mal en el Django.Django (?) Realmente lento con grandes conjuntos de datos después de hacer un perfil de python

Primero, algo de contexto: tengo una página que arroja informes de datos de ventas. Los datos se pueden filtrar por varias cosas, pero en su mayoría están filtrados por fecha. Esto hace que sea un poco difícil de almacenar en caché, ya que las posibilidades de resultados son casi infinitas. Hay muchos números y cálculos realizados, pero nunca fue un gran problema manejarlo dentro de PHP.

ACTUALIZACIONES:

  • Después de algunas pruebas adicionales que no hay nada dentro de mi opinión de que la causa de la desaceleración. Si simplemente estoy analizando los datos y escupiendo 5 filas de HTML renderizado, no es tan lento (aún más lento que PHP), pero si estoy procesando una gran cantidad de datos, es MUY lento.

  • Cada vez que ejecuté un informe grande (por ejemplo, todas las ventas del año), el uso de la CPU de la máquina es del 100%. No sé si esto significa mucho. Estoy usando mod_python y Apache. Tal vez cambiar a WSGI puede ayudar?

  • Mis etiquetas de plantilla que muestran los subtotales/totales procesan desde 0,1 segundos hasta 1 segundo para conjuntos muy grandes. Los llamo alrededor de 6 veces en el informe, por lo que no parecen el mayor problema.

Ahora, me encontré con un perfilador de Python y volvió con estos resultados:

 
Ordered by: internal time 
    List reduced from 3074 to 20 due to restriction 

    ncalls tottime percall cumtime percall filename:lineno(function) 
    2939417 26.290 0.000 44.857 0.000 /usr/lib/python2.5/tokenize.py:212(generate_tokens) 
    2822655 17.049 0.000 17.049 0.000 {built-in method match} 
    1689928 15.418 0.000 23.297 0.000 /usr/lib/python2.5/decimal.py:515(__new__) 
12289605 11.464 0.000 11.464 0.000 {isinstance} 
    882618 9.614 0.000 25.518 0.000 /usr/lib/python2.5/decimal.py:1447(_fix) 
    17393 8.742 0.001 60.798 0.003 /usr/lib/python2.5/tokenize.py:158(tokenize_loop) 
     11 7.886 0.717 7.886 0.717 {method 'accept' of '_socket.socket' objects} 
    365577 7.854 0.000 30.233 0.000 /usr/lib/python2.5/decimal.py:954(__add__) 
    2922024 7.199 0.000 7.199 0.000 /usr/lib/python2.5/inspect.py:571(tokeneater) 
    438750 5.868 0.000 31.033 0.000 /usr/lib/python2.5/decimal.py:1064(__mul__) 
    60799 5.666 0.000 9.377 0.000 /usr/lib/python2.5/site-packages/django/db/models/base.py:241(__init__) 
    17393 4.734 0.000 4.734 0.000 {method 'query' of '_mysql.connection' objects} 
    1124348 4.631 0.000 8.469 0.000 /usr/lib/python2.5/site-packages/django/utils/encoding.py:44(force_unicode) 
    219076 4.139 0.000 156.618 0.001 /usr/lib/python2.5/site-packages/django/template/__init__.py:700(_resolve_lookup) 
    1074478 3.690 0.000 11.096 0.000 /usr/lib/python2.5/decimal.py:5065(_convert_other) 
    2973281 3.424 0.000 3.424 0.000 /usr/lib/python2.5/decimal.py:718(__nonzero__) 
    759014 2.962 0.000 3.371 0.000 /usr/lib/python2.5/decimal.py:4675(__init__) 
    381756 2.806 0.000 128.447 0.000 /usr/lib/python2.5/site-packages/django/db/models/fields/related.py:231(__get__) 
    842130 2.764 0.000 3.557 0.000 /usr/lib/python2.5/decimal.py:3339(_dec_from_triple) 

tokenize.py viene a la cabeza, que pueden hacer un cierto sentido como lo estoy haciendo una gran cantidad de formato de número . Decimal.py tiene sentido ya que el informe es esencialmente un 90% de números. No tengo idea de cuál es el método incorporado match, ya que no estoy haciendo ningún Regex o similar en mi propio código (¿Algo está haciendo Django?) Lo más parecido es que estoy usando itertools ifilter.

Parece que esos son los principales culpables y si pudiera encontrar la manera de reducir el tiempo de procesamiento de aquellos, tendría una página mucho más rápida.

¿Alguien tiene alguna sugerencia sobre cómo podría comenzar a reducir esto? Realmente no sé cómo solucionaría esto los problemas de tokenize/decimal sin simplemente eliminarlos.

Actualización: Realicé algunas pruebas con/sin filtros en la mayoría de los datos y los tiempos de los resultados fueron prácticamente los mismos, el último fue un poco más rápido pero no fue la causa del problema. ¿Qué está pasando exactamente en tokenize.py?

+0

Es imposible sugerir algo útil sin su código de vista y guía de generación de perfiles. –

+0

Alex: Mi opinión es bastante simple. Retira una lista inicial de entradas, luego, si se modifica el informe, agrega algunos filtros. Eso es realmente todo. Mi plantilla luego reagrupa el conjunto de datos en dos secciones y luego recorre todo, llamando listones en el camino (Pero he programado las etiquetas de la plantilla para que se ejecuten en 0.1 -> 0.5 segundos ... estas plantillas list son los subtotales/totales del informe para que el tiempo de ejecución sea aceptable en grandes conjuntos de datos.) – Bartek

+0

@Bartek: por favor, no comenten su propia pregunta. Es su pregunta, puede actualizarla para contener todos los hechos relevantes. –

Respuesta

6

Hay muchas cosas que asumir sobre su problema ya que no tiene ningún tipo de código de muestra.

He aquí mis suposiciones: está utilizando las herramientas y modelos ORM integrados de Django (es decir, sales-data = modelobj.objects(). All()) y en el lado de PHP está tratando con consultas SQL directas y trabajando con un query_set.

Django realiza muchos tipos de conversión y conversión a tipos de datos que van desde una consulta de base de datos en el objeto ORM/Modelo y el administrador asociado (objetos() de forma predeterminada).

En PHP usted está controlando las conversiones y sabe exactamente cómo transmitir de un tipo de datos a otro, está ahorrando tiempo de ejecución solo en función de ese problema.

Recomendaría intentar mover parte de ese trabajo de números sofisticado a la base de datos, especialmente si está haciendo un proceso basado en el conjunto de registros - las bases de datos comen ese tipo de procesamiento desde el desayuno. En Django puede enviar RAW SQL a la base de datos: http://docs.djangoproject.com/en/dev/topics/db/sql/#topics-db-sql

espero que esto al menos se puede conseguir que apunta en la dirección correcta ...

+0

Gracias. Tienes razón, eso tiene sentido. Estaba viendo consultas ejecutando bien y con baja MS así que nunca lo consideré. El problema es, por supuesto, que el ORM es encantador y mantiene el código mucho más limpio en casos como este, así que me gustaría no tener que ir por ese camino si es posible. El procesamiento de números que estoy haciendo no es complejo (sume estos tres números, multiplique esto) y luego simplemente los estoy sacando usando los filtros | intcomma y | floatformat: 2 django, así que no estoy seguro si ese es el núcleo del problema. – Bartek

+0

El problema con el que se puede encontrar para agregar números, multiplicar números es la cantidad de registros. Si mantiene el número de registros devueltos más bajos, se reducirá la sobrecarga de memoria y la cantidad de tiempo que lleva procesar esos datos. Tenga esto en cuenta: no puede hacer que una aplicación sea más rápida, solo puede hacer que haga menos trabajo. –

+0

Desafortunadamente en algunos casos severos, el usuario quiere un informe de datos de ventas anuales y realmente no puedo mantener los registros más bajos. .. :) – Bartek

2

"tokenize.py viene a la cabeza, lo que puede hacer un poco sentido, ya que estoy haciendo un montón de formateo de números. "

No tiene sentido en absoluto.

Ver http://docs.python.org/library/tokenize.html.

El módulo tokenize proporciona un léxico escáner de código fuente de Python, implementado en Python

Tokenize que sale en la parte superior significa que tiene el análisis de código dinámico pasando.

AFAIK (haciendo una búsqueda en el repositorio Django) Django no usa tokenize. Entonces, eso hace que su programa realice algún tipo de creación de instancias de código dinámico. O bien, solo está perfilando el primero vez que su programa se carga, analiza y ejecuta, lo que lleva a suposiciones falsas sobre hacia dónde va el tiempo.

Debe no hacer siempre el cálculo en las etiquetas de la plantilla - es lento. Implica una metaevaluación compleja de la etiqueta de la plantilla. Debería hacer todos los cálculos en la vista en Python simple y de poca actividad. Use las plantillas para presentación solamente.

Además, si constantemente está haciendo consultas, filtros, sumas y otras cosas, tiene un almacén de datos. Obtenga un libro sobre el diseño del depósito de datos y siga los patrones de diseño del almacén de datos.

Debe tener una tabla de hechos central, rodeada de tablas de dimensiones. Esto es muy, muy eficiente.

Sumas, grupos bys, etc., se pueden realizar como operaciones defaultdict en Python. A granel busque todas las filas y cree el diccionario con los resultados deseados. Si esto es demasiado lento, entonces tiene que usar técnicas de almacenamiento de datos para guardar sumas y grupos persistentes por separado de sus datos detallados. A menudo esto implica salir del ORM de Django y usar las funciones RDBMS como vistas o tablas de datos derivados.

+0

¿Puede decirme por qué no debo hacer ningún cálculo (además de sumar los números) en templatetags? Después de su publicación, descubrí una manera más eficiente de hacer lo que están haciendo mis plantillas, lo cual es útil, pero aún no es el cuello de botella. Al menos se ralentizará unos segundos de tiempo de procesamiento :) Gracias – Bartek

2

Cuando se trata de grandes conjuntos de datos, también puede ahorrar mucha CPU y memoria usando ValuesQuerySet que accede a los resultados de la consulta más directamente en lugar de crear una instancia de objeto modelo para cada fila en el resultado.

el uso de lo que parece un poco como esto:

Blog.objects.order_by('id').values() 
1

En tal escenario, la base de datos es a menudo el cuello de botella. Además, el uso de un ORM puede dar lugar a consultas SQL subóptimas.

Como algunos señalaron, no es posible determinar qué es exactamente el análisis, solo con la información que nos proporcionó.

yo sólo te puedo dar algunos consejos generales:

  • Si su vista está trabajando con objetos relacionados con el modelo, considere el uso select_related(). Este método simple puede acelerar las consultas generadas por el ORM considerablemente.
  • Utilice Debug Footer Middleware para ver qué consultas SQL generan sus vistas y qué tiempo tardaron en ejecutarse.

PD: Solo fyi, tuve una vez una vista bastante simple que era muy lenta. Después de instalar el Debug Footer Middleware ¡vi eso alrededor de 500! las consultas sql se ejecutaron en esa vista única. El solo uso de select_related() redujo a 5 consultas y la vista se realizó como se esperaba.

Cuestiones relacionadas