2009-03-17 8 views
3

Ich versuche, einige Python-Code zu optimieren. Der Profiler sagt mir, dass SQLAlchemy's _get_col() ist, was die Leistung killt. Der Code sieht etwa so aus:Kann ich Zeilen von SQLAlchemy erhalten, die einfache Arrays statt Wörterbücher sind?

lots_of_rows = get_lots_of_rows() 
for row in lots_of_rows: 
    if row.x == row.y: 
     print row.z 

ich war durch den Code gehen und es so mehr machen ...

lots_of_rows = get_lots_of_rows() 
for row in lots_of_rows: 
    if row[0] == row[1]: 
     print row[2] 

... aber ich habe einige Unterlagen gefunden, scheint um anzuzeigen, dass Sie beim Zugriff auf Zeilenobjekte wie Arrays immer noch Wörterbuchschlüssel ziehen. Mit anderen Worten sieht die Zeile Objekt wie folgt:

'x': (x object) 
'0': (x object) 
'y': (y object) 
'1': (y object) 
'z': (z object) 
'2': (z object) 

Wenn das der Fall ist, bezweifle ich, dass ich von jeder Leistungssteigerung sehen werden Spalten von Nummer anstatt seinen Namen zugreift. Gibt es eine Möglichkeit, SA zu veranlassen, Ergebnisse als eine Liste von Tupeln oder eine Liste von Listen anstelle einer Liste von Wörterbüchern zurückzugeben? Kann jemand andere Optimierungen vorschlagen?

+0

Ein Hauptziel für SQL Alchemy ist es, die Dinge hinzuzufügen, die Sie loswerden wollen. Vielleicht wäre es sinnvoll, für diese Abfrage auf den mysqldb/dbapi-Layer zuzugreifen? Selbst wenn nicht, würde ich empfehlen, eine ähnliche Abfrage mit nur dem mysqldb-Treiber zu rekodieren, um zu sehen, wie viel Leistung Sie wirklich erzielen könnten. –

Antwort

1

Ich denke, row.items() ist, was Sie suchen. Sie gibt eine Liste von (Schlüssel-, Wert-) Tupeln für die Zeile zurück.

Link

+0

Leider ist items() nur das: return [(key, getattr (self, key)) für den Schlüssel in self.keys()] ... und __getattr __() ruft _get_col() auf. Es ist also sogar noch langsamer. – mike

2

die offensichtliche Antwort vergeben, aber warum == row.y in Ihrer Abfrage nicht row.x ist? Zum Beispiel:

mytable.select().where(mytable.c.x==mytable.c.y) 

Sollten Sie einen riesigen Leistungsschub geben. Read the rest of the documentation.

+0

Ich denke, dass mein vereinfachtes Beispiel zu stark vereinfacht ist. :) Der eigentliche Filter ist komplexer als das. – mike

+1

Dann lass uns mal die echte Sache sehen. Im Allgemeinen, wenn Sie Daten aus einer Datenbank erhalten und sie dann zum Filtern durchlaufen, tun Sie wahrscheinlich etwas Abscheuliches. –

+0

In Wirklichkeit ist der Filter ein komplexer regulärer Ausdruck. – mike

0

Sie sollten Ihre Profiler-Ergebnisse sowie Stack-Traces um den '_get_col' Aufruf, so dass wir wissen, welche _get_col aufgerufen wird. (und ob _get_col wirklich der Flaschenhals ist).

Ich schaute auf die Quelle sqlalchemy, sieht aus wie es Aufruf 'Lookup_key' (in engine/base.py) jedes Mal und es sieht so aus, als speichere die Spalte Wert lokal, ich denke träge (über PopulateDict).

Sie können versuchen, das zu umgehen, indem Sie direkt mit einem Cursor.

- J

1

SQLAlchemy Proxies alle Zugriff auf den Cursor zugrunde liegenden Datenbank mit dem Namen Schlüssel zu Positionen in der Reihe Tupels mappen und alle erforderlichen Typkonvertierungen durchführen. Die zugrundeliegende Implementierung ist ziemlich stark optimiert, Caching fast alles. Wenn man sich die Disassemblierung ansieht, scheint es nur möglich zu sein, die Erweiterbarkeit zu beseitigen und einige Attribut-Lookups zu entfernen oder auf die dynamische Code-Generierung für kleinere Gewinne zurückzugreifen oder die entsprechenden ResultProxy- und RowProxy-Klassen zu implementieren C.

Einige schnelle Profiling zeigt, dass der Overhead rund 5us pro Lookup auf meinem Laptop ist. Das wird signifikant sein, wenn nur eine triviale Verarbeitung mit den Daten durchgeführt wird. In solchen Fällen kann es sinnvoll sein, auf dbapi-Ebene zu wechseln. Dies bedeutet nicht, dass Sie die Funktionalität zum Erstellen von Abfragen von SQLAlchemy verlieren müssen. Führen Sie die Anweisung wie gewohnt aus und rufen Sie den Dbapi-Cursor von ResultProxy ab, indem Sie auf result.cursor.cursor zugreifen. (result.cursor ist ein SQLAlchemy CursorFairy-Objekt) Dann können Sie die regulären Methoden dbapi fetchall(), fetchone() und fetchmany() verwenden.

Aber wenn Sie wirklich trivial Verarbeitung tun, könnte es sinnvoll sein, oder zumindest die Filterung Teil auf dem Datenbankserver zu tun. Sie verlieren wahrscheinlich die Portabilität der Datenbank, aber das ist möglicherweise kein Problem.