2009-07-23 8 views
5

Ich habe ein altes PHP-Skript von mir gegen die neuere, schickere Django-Version und die PHP-Version, mit vollem Ausspucken von HTML und alles funktionierte schneller. VIEL schneller bis zu dem Punkt, dass auf dem Django etwas nicht stimmt.Django (?) Wirklich langsam mit großen Datensätzen nach einigen Python-Profiling

Zuerst einige Kontext: Ich habe eine Seite, die Berichte über Verkaufsdaten ausspuckt. Die Daten können nach einer Anzahl von Dingen gefiltert werden, werden jedoch meistens nach Datum gefiltert. Dies macht es ein wenig schwierig, es zu cachen, da die Möglichkeiten für Ergebnisse fast endlos sind. Es gibt viele Zahlen und Berechnungen, aber es war nie ein Problem innerhalb von PHP.

UPDATES:

  • Nach einigen weiteren Tests nichts in meiner Sicht gibt es, dass die Verlangsamung verursacht. Wenn ich die Daten einfach nummeriere und 5 Zeilen gerenderten HTML ausspucke, ist es nicht so langsam (immer noch langsamer als PHP), aber wenn ich viele Daten rendere, ist es SEHR langsam.

  • Immer wenn ich einen großen Bericht (z. B. alle Verkäufe für das Jahr) ausgeführt habe, geht die CPU-Auslastung des Systems zu 100%. Ich weiß nicht, ob das viel bedeutet. Ich benutze mod_python und Apache. Vielleicht kann der Wechsel zu WSGI helfen?

  • Meine Vorlage-Tags, die die Zwischensummen/Gesamtwerte zwischen 0,1 Sekunden und 1 Sekunde für wirklich große Mengen anzeigen. Ich rufe sie etwa 6 Mal innerhalb des Berichts an, also scheinen sie nicht das größte Problem zu sein.

Nun lief ich ein Python-Profiler und kam mit diesen Ergebnissen zurück:

 
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 oben kommt, die einen Sinn machen kann, wie ich eine Menge Nummer tue Formatierung . Decimal.py macht Sinn, da der Bericht im Wesentlichen zu 90% nummeriert ist. Ich habe keine Ahnung, was die eingebaute Methode match ist, da ich keine Regex oder ähnliches in meinem eigenen Code mache (Etwas, was Django macht?) Das nächste ist, dass ich itertools ifilter verwende.

Es scheint, dass diese die Hauptschuldigen sind und wenn ich herausfinden könnte, wie man die Bearbeitungszeit jener verringert, dann hätte ich eine viel viel schnellere Seite.

Hat jemand irgendwelche Vorschläge, wie ich anfangen könnte, dies zu reduzieren? Ich weiß nicht wirklich, wie ich das Problem mit Tokenize/Decimal lösen würde, ohne es einfach zu entfernen.

Update: Ich habe einige Tests mit/ohne Filter auf den meisten Daten durchgeführt und die Ergebniszeiten sind ziemlich gleich geblieben, wobei letzteres ein bisschen schneller ist, aber nicht viel, um die Ursache des Problems zu sein. Was genau passiert in tokenize.py?

+0

Es ist unmöglich, etwas Nützliches ohne Ihre Ansicht Code und Profiling Anleitung vorschlagen. –

+0

Alex: Meine Sicht ist ziemlich einfach. Es wird eine erste Liste mit Einträgen angezeigt. Wenn der Bericht geändert wird, werden einige Filter hinzugefügt. Das ist es wirklich. Meine Vorlage gruppiert das Dataset dann in zwei Abschnitte und führt dann einen Rundlauf durch alle durch und ruft templatetags auf dem Weg auf. (Aber ich habe die Template-Tags in 0.1 -> 0.5 Sekunden getaktet. Diese Templatetags sind die Zwischensummen/Summen des Berichts so dass die Ausführungszeit bei großen Datenmengen in Ordnung ist.) – Bartek

+0

@Bartek: Bitte kommentieren Sie nicht Ihre eigene Frage. Es ist Ihre Frage, Sie können es aktualisieren, um alle relevanten Fakten zu enthalten. –

Antwort

6

Es gibt eine Menge Dinge über Ihr Problem zu wissen, da Sie keine Art von Codebeispiel haben.

Hier sind meine Annahmen: Sie verwenden Django integrierte ORM-Tools und -Modelle (dh Verkaufsdaten = modelobj.objects(). All()) und auf der PHP-Seite beschäftigen Sie sich mit direkten SQL-Abfragen und arbeiten mit einem query_set.

Django macht eine Menge Konvertierung und Umwandlung in Datentypen, die von einer Datenbankabfrage in das ORM/Model-Objekt und den zugehörigen Manager (objects()) gehen.

In PHP steuern Sie die Konvertierungen und wissen genau, wie man von einem Datentyp in einen anderen umwandelt, Sie sparen etwas Ausführungszeit basierend auf diesem Problem.

Ich würde empfehlen zu versuchen, einige dieser Phantasie Zahlenarbeit in die Datenbank zu verschieben, vor allem, wenn Sie Datensatz-basierte Verarbeitung tun - Datenbanken essen diese Art der Verarbeitung vom Frühstück. In Django können Sie RAW-SQL über an die Datenbank senden: http://docs.djangoproject.com/en/dev/topics/db/sql/#topics-db-sql

ich das zumindest hoffen, dass Sie in die richtige Richtung wies ...

+0

Danke. Du hast recht, das macht Sinn. Ich habe gesehen, dass Abfragen gut und mit niedriger MS ausgeführt wurden, also habe ich das nie berücksichtigt. Das Problem ist natürlich, dass das ORM sehr schön ist und den Code in solchen Fällen viel sauberer hält, so dass ich es genießen würde, diese Straße möglichst nicht zu verlassen. Die Zahlenverarbeitung, die ich mache, ist nicht komplex (addiere diese drei Zahlen, multipliziere das) und gebe sie dann einfach mit den | intcomma und | floatformat: 2 Django-Filtern aus, so dass ich nicht sicher bin, ob das der Kern des Problems ist. – Bartek

+0

Das Problem, das Sie möglicherweise in Bezug auf das Hinzufügen von Zahlen, Multiplizieren von Zahlen, ist die Anzahl der Datensätze. Wenn Sie die Anzahl der zurückgegebenen Datensätze verringern, verringert sich der Speicheraufwand und die für die Verarbeitung dieser Daten benötigte Zeit. Denken Sie daran: Sie können eine Bewerbung nicht schneller machen, Sie können nur weniger Arbeit machen. –

+0

Leider möchte der Benutzer in einigen schweren Fällen einen Bericht über die jährlichen Verkaufsdaten haben, und ich kann die Datensätze nicht wirklich senken. .. :) – Bartek

2

„tokenize.py oben kommt, was einige machen Sinn, da ich eine Menge Zahlenformatierung mache. "

Macht überhaupt keinen Sinn.

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

Das tokenize Modul bietet einen lexikalischen Scanner für Python-Quellcode, in Python implementiert

tokenize oben herauskommen bedeutet, dass Sie dynamische Code-Analyse geht haben.

AFAIK (Suche im Django-Repository) Django verwendet keine Tokenize. Das bewirkt, dass Ihr Programm eine Art dynamische Code-Instanziierung ausführt. Oder Sie profilieren nur die erste Zeit, die Ihr Programm geladen, geparst und ausgeführt wird, was zu falschen Annahmen darüber führt, wo die Zeit ist.

Sie sollten nicht jemals Berechnung in Vorlage Tags tun - es ist langsam. Es beinhaltet eine komplexe Meta-Auswertung des Template-Tags. Sie sollten alle Berechnungen in der Ansicht in Python mit geringem Overhead durchführen. Verwenden Sie die Vorlagen nur für die Präsentation.

Auch wenn Sie ständig Abfragen, Filter, Summen und was nicht tun, haben Sie ein Data Warehouse. Erstellen Sie ein Buch zum Data Warehouse-Design und folgen Sie den Data Warehouse-Entwurfsmustern.

Sie müssen eine zentrale Faktentabelle haben, die von Dimensionstabellen umgeben ist. Das ist sehr, sehr effizient.

Summen, Gruppen-Bys usw. können als defaultdict Operationen in Python durchgeführt werden. Bulk holt alle Zeilen und erstellt das Wörterbuch mit den gewünschten Ergebnissen. Wenn dies zu langsam ist, müssen Sie Data Warehousing-Techniken verwenden, um persistente Summen und Gruppen getrennt von Ihren feinkörnigen Fakten zu speichern. Oft beinhaltet dies das Verlassen des Django-ORM und die Verwendung von RDBMS-Funktionen wie Ansichten oder Tabellen abgeleiteter Daten.

+0

Können Sie mir sagen, warum ich in Templatetags keine Berechnungen (Grundaddition zu Summenzahlen) machen soll? Nach deinem Post habe ich einen effizienteren Weg gefunden, um das zu tun, was meine Template-Tags tun, was zwar nützlich ist, aber immer noch nicht der Flaschenhals ist. Atleast wird es ein paar Sekunden Bearbeitungszeit rasieren :) Danke – Bartek

2

Bei großen Datenmengen können Sie auch viel CPU und Arbeitsspeicher sparen, indem Sie ValuesQuerySet verwenden, die direkter auf die Abfrageergebnisse zugreift, anstatt eine Modellobjektinstanz für jede Zeile im Ergebnis zu erstellen.

Es ist Nutzung sieht ein bisschen wie folgt aus:

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

In einem solchen Szenario die Datenbank ist oft der Engpass. Außerdem kann die Verwendung eines ORM zu suboptimalen SQL-Abfragen führen.

Wie einige darauf hingewiesen haben, ist es nicht möglich zu sagen, was das Problem wirklich ist, nur mit den Informationen, die Sie zur Verfügung gestellt haben.

Ich kann Ihnen nur einige allgemeine Ratschläge geben:

  • Wenn Sie die Ansicht mit ähnlichen Modellobjekten funktioniert, betrachten select_related() verwenden. Diese einfache Methode könnte die vom ORM generierten Abfragen erheblich beschleunigen.
  • Verwenden Sie die Debug Footer Middleware, um zu sehen, welche SQL-Abfragen von Ihren Ansichten generiert werden und welche Zeit sie für die Ausführung benötigt haben.

PS: Nur fyi, ich hatte einmal eine ziemlich einfache Ansicht, die sehr langsam war. Nach der Installation der Debug Footer Middleware sah ich, dass rund 500! SQL-Abfragen wurden in dieser einzelnen Ansicht ausgeführt. Mit nur select_related() brachte dies auf 5 Abfragen und die Ansicht wie erwartet durchgeführt.

Verwandte Themen