Optimierung in Python - Do's, Don'ts und Faustregeln

8

Nun, ich habe diesen Post gelesen und dann Ich stieß auf einen Code, der war:

%Vor%

Ich dachte, wäre es nicht besser, den Wert von len (Witze) einmal außerhalb des Listenverständnisses zu berechnen?

Nun, ich habe es versucht und drei Codes timed

%Vor%

Beobachten Sie den marginalen Unterschied von 2,55% zwischen der ersten und der zweiten hat mich denken lassen - ist das erste Listenverständnis

%Vor%

intern von Python optimiert? oder ist 2,55% eine groß genug Optimierung (angesichts der Len (Witze) = 1000000)?

Wenn das - Was sind die anderen impliziten / internen Optimierungen in Python?

Was sind die developer's rules of thumb for optimization in Python ?

Edit1 : Da die meisten Antworten "nicht optimieren, mach es später, wenn es langsam ist" und ich habe ein paar Tipps und Links von Triptych und Ali A für das Mach's . Ich werde die Frage ein wenig ändern und nach Don'ts fragen.

Können wir einige Erfahrungen von Menschen machen, die mit der Langsamkeit konfrontiert waren, was war das Problem und wie wurde es korrigiert?

Edit2 : Für diejenigen, die nicht hier sind, ist eine interessante Lektüre

Edit3: Falsche Verwendung von timeit in Frage, bitte sehen Sie dF's Antwort für die korrekte Verwendung und damit Timings für die drei Codes.

    
JV. 31.12.2008, 18:57
quelle

8 Antworten

12

Sie verwenden timeit nicht richtig: Das Argument für -s (setup) ist a Anweisung, die einmal ausgeführt wird. Sie testen also nur eine leere Anweisung. Du willst

machen %Vor%

Während die Beschleunigung noch nicht dramatisch ist, ist sie signifikanter (16% bzw. 25%). Da es den Code nicht komplizierter macht, lohnt sich diese einfache Optimierung wahrscheinlich.

Um die eigentliche Frage zu beantworten ... ist die übliche Faustregel in Python

  1. Bevorzugung von klarem und lesbarem Code gegenüber Optimierung beim Codieren.

  2. Schreiben Sie Ihren Code ( profile / cProfile und pstats sind Ihre Freunde), um herauszufinden Was Sie optimieren müssen (normalerweise Dinge wie enge Schleifen).

  3. Als letzten Ausweg, implementieren Sie diese als C-Erweiterungen, die mit Tools wie Pyrex und Cython .

Eine Sache, auf die Sie achten sollten: Im Vergleich zu vielen anderen Sprachen sind Funktionsaufrufe in Python relativ teuer, weshalb die Optimierung in Ihrem Beispiel einen Unterschied machte, obwohl len O (1) für Listen ist.

    
dF. 31.12.2008, 19:13
quelle
4

Lesen Sie hierzu: Python-Geschwindigkeits- / Leistungstipps

Auch in Ihrem Beispiel ist die Gesamtzeit so kurz, dass die Fehlermarge jeden tatsächlichen Geschwindigkeitsunterschied überwiegt.

    
Triptych 31.12.2008 19:05
quelle
2

Dies gilt für die gesamte Programmierung, nicht nur für Python:

  1. Profil
  2. Identifizieren Sie Engpässe
  3. Optimieren

Und ich würde sogar hinzufügen, dass ich mich nicht darum kümmern muss, es sei denn, Sie haben ein Langsamkeitsproblem, das Ihnen Schmerzen bereitet.

Und vielleicht am wichtigsten ist, dass Komponententests Ihnen während des tatsächlichen Prozesses helfen werden.

    
Ali Afshar 31.12.2008 19:18
quelle
1

Bei einer tangential verwandten Anmerkung ist die Verkettung von Generatoren viel effizienter als die Verkettung von Listenkomprehensionen und oft intuitiver.

Was die Faustregeln des Entwicklers für die Optimierung in Python betrifft, sind sie die gleichen wie in allen anderen Sprachen.

  1. Nicht optimieren.
  2. (fortgeschritten) Optimiere später.
Brian 31.12.2008 19:03
quelle
1

len für Listen ist O (1). Es muss nicht die gesamte Liste gescannt werden, um die Länge zu finden, da die Größe der Liste für die Suche gespeichert wird. Aber anscheinend ist es noch etwas schneller, es in eine lokale Variable zu extrahieren.

Um Ihre Frage zu beantworten, würde ich mich jedoch niemals um Leistungsschwankungen in der Größenordnung von 5% kümmern, es sei denn, ich würde in einer Simulation oder so etwas verrückte, enge innere Schleifenoptimierungen vornehmen. Und in diesem Fall könnten Sie das viel schneller beschleunigen, indem Sie überhaupt keine Reichweite verwenden.

    
recursive 31.12.2008 19:05
quelle
1

Das Wichtigste ist, idiomatischen, klaren und schönen Python-Code zu schreiben. Viele häufig vorkommende Aufgaben sind bereits in der stdlib enthalten, so dass Sie keine langsamere Version neu schreiben müssen. (Ich denke hier speziell an String-Methoden und Iter-Tools.) Nutzen Sie auch die eingebauten Container von Python. dict zum Beispiel hat "the snot optimized" daraus gemacht, und es wird gesagt, dass Python-Code mit dicts schneller ist als normales C!

Wenn das noch nicht schnell genug ist, gibt es einige Hacks, die Sie verwenden können, aber es ist auch ein Signal, dass Sie wahrscheinlich etwas Arbeit an C-Erweiterungsmodule abladen sollten.

Betreffend Listen-Comprehensions: CPython kann einige Optimierungen gegenüber regulären Akkumulator-Schleifen vornehmen. Der Operationscode LIST_APPEND fügt sich nämlich an eine native Operation der Liste an.

    
Benjamin Peterson 31.12.2008 19:22
quelle
1

Ich habe ein Programm, das Protokolldateien analysiert und ein Data Warehouse generiert. Ein typischer Lauf umfasst rund 200 Millionen Protokolldateizeilen und läuft den größten Teil eines Tages. Lohnt sich zu optimieren!

Da es sich um einen Parser handelt, der einen ziemlich variablen und idiosynkratischen und nicht vertrauenswürdigen Text parst, gibt es ungefähr 100 reguläre Ausdrücke, die pflichtbewusst re.compiled () im Voraus sind und auf jede der 200M-Protokolldateizeilen angewendet werden. Ich war mir ziemlich sicher, dass sie mein Engpass waren und überlegte, wie ich diese Situation verbessern könnte. Hatte einige Ideen: auf der einen Seite, weniger, Züchter RE; auf der anderen, mehr und einfacher; solche Sachen.

Ich profilierte mit CProfile und schaute auf das Ergebnis in "runnsnake".

RE-Verarbeitung war nur etwa 10% der Code-Ausführungszeit. Das ist es nicht!

In der Tat sagte mir ein großer quadratischer Fleck in der Runnnake-Anzeige sofort, dass ungefähr 60% meiner Zeit in einer der berüchtigten "One Line Changes" verbracht wurde, die ich eines Tages hinzugefügt hatte gelegentlich erscheinen, aber immer etwas so falsch darstellen, ich interessiere mich wirklich nicht dafür). Dies verwirrte meine Syntaxanalyse und das Auslösen von Ausnahmen, die ich getan habe, weil es meinen Tag der Protokolldateianalyse angehalten hat.

  

line = '' .join ([c für c in curse.ascii.isprint (c)])

Da geht's los: Diese Zeile berührt jedes Byte jeder dieser 200M-Zeilen (und die Zeilen sind durchschnittlich einige hundert Byte lang). Kein Wunder, dass es 60% meiner Ausführungszeit ist!

Es gibt bessere Möglichkeiten, damit umzugehen, wie ich jetzt weiß, zB str.translate (). Aber solche Zeilen sind selten, und ich kümmere mich sowieso nicht darum, und sie werfen am Ende eine Ausnahme auf: Jetzt erwische ich einfach die Ausnahme an der richtigen Stelle und überspringe die Zeile. Voila! Das Programm ist 3x schneller, sofort!

Also das Profiling

  1. in etwa einer Sekunde hervorgehoben, wo das Problem tatsächlich war
  2. lenkte meine Aufmerksamkeit von einer falschen Annahme ab, wo das Problem lag (was die noch größere Belohnung sein könnte)
jackr 07.05.2010 22:18
quelle
0
  

Können wir einige Erfahrungen von   Menschen, die der "Langsamkeit" gegenüberstanden, was   war das Problem und wie es war   korrigiert?

Das Problem war ein langsamer Datenabruf in einer GUI-App. Ich habe eine 50-fache Beschleunigung durch Hinzufügen eines Indexes zum Tisch erreicht und wurde weithin als Held und Retter gepriesen.

    
recursive 31.12.2008 20:52
quelle

Tags und Links