Warum Python ist so langsam für eine einfache für Schleife?

Wir machen einige kNN und SVD Implementierungen in Python. Andere haben Java ausgewählt. Unsere Ausführungszeiten sind sehr unterschiedlich. Ich habe cProfile benutzt, um zu sehen, wo ich Fehler mache, aber alles ist ganz gut . Ja, ich numpy auch numpy Aber ich möchte gern eine einfache Frage stellen.

 total = 0.0 for i in range(9999): # xrange is slower according for j in range(1, 9999): #to my test but more memory-friendly. total += (i / j) print total 

Dieses Snippet nimmt 31.40s auf meinem Computer.

Java-Version dieses Codes dauert 1 Sekunde oder weniger auf demselben Computer. Typ-Checking ist ein Hauptproblem für diesen Code, nehme ich an. Aber ich sollte so viel für dieses Projekt machen und ich denke 9999 * 9999 ist nicht so groß.

Ich glaube, ich mache Fehler, weil ich weiß, dass Python von vielen wissenschaftlichen Projekten benutzt wird. Aber warum ist dieser Code so langsam und wie kann ich mit größeren Problemen umgehen?

Soll ich einen JIT-Compiler wie Psyco ?

BEARBEITEN

Ich sage auch, dass dieses Loop-Problem nur ein Beispiel ist. Der Code ist nicht so einfach wie so und es kann schwierig sein, Ihre Verbesserungen / Code-Samples in die Praxis umzusetzen.

Eine andere Frage ist, dass kann ich viele Data Mining & Machine Lernalgorithmen mit numpy und scipy wenn ich es richtig verwenden?

9 Solutions collect form web for “Warum Python ist so langsam für eine einfache für Schleife?”

Ich glaube, ich mache Fehler, weil ich weiß, dass Python von vielen wissenschaftlichen Projekten benutzt wird.

Sie sind stark mit SciPy (NumPy ist die prominenteste Komponente, aber ich habe gehört, dass das Ökosystem, das sich um NumPys API entwickelt hat, noch wichtiger ist), was die ganze Art von Operationen, die diese Projekte benötigen, erheblich beschleunigt. Es gibt, was du falsch machst: Du schreibst nicht deinen kritischen Code in C. Python ist ideal für die Entwicklung im Allgemeinen, aber gut platzierte Erweiterungsmodule sind eine entscheidende Optimierung in seinem eigenen Recht (zumindest wenn Sie knacken Zahlen) . Python ist eine wirklich beschissene Sprache, um enge innere Schleifen zu implementieren.

Die Voreinstellung (und für die zeitlich beliebteste und weitgehend unterstützte) Implementierung ist ein einfacher Bytecode-Interpreter. Sogar die einfachsten Operationen, wie eine ganzzahlige Division, können Hunderte von CPU-Zyklen, mehrere Speicherzugriffe (Typprüfungen, die ein beliebtes Beispiel sind), mehrere C-Funktionsaufrufe usw. statt ein paar (oder sogar Single, im Fall von Integer) Division) Anweisung. Darüber hinaus ist die Sprache mit vielen Abstraktionen entworfen, die Overhead hinzufügen. Ihre Schleife verteilt 9999 Objekte auf dem Heap, wenn Sie xrange verwenden – weit mehr, wenn Sie range (9999 * 9999 Integer minus um 256 * 256 für kleine ganze Zahlen, die zwischengespeichert werden). Auch die xrange Version ruft eine Methode auf jeder Iteration auf, um voranzukommen – die range wäre auch, wenn Iteration über Sequenzen nicht speziell optimiert wurde. Es dauert aber immer noch einen ganzen Bytecode-Versand, der selbst sehr komplex ist (im Vergleich zu einer ganzzahligen Division natürlich).

Es wäre interessant zu sehen, was ein JIT (ich würde empfehlen PyPy über Psyco, das letztere ist nicht aktiv mehr entwickelt und sehr begrenzt in Umfang sowieso – es könnte gut funktionieren für dieses einfache Beispiel aber). Nach einem winzigen Bruchteil von Iterationen sollte es eine nahezu optimale Maschinencodeschleife erzeugen, die mit ein paar Wachen ergänzt wird – einfache ganzzahlige Vergleiche, Springen, wenn sie scheitern – die Korrektheit beibehalten, falls Sie einen String in dieser Liste erhalten haben. Java kann das Gleiche tun, nur früher (es muss nicht zuerst nachspüren) und mit weniger Wachen (zumindest wenn man int s benutzt). Deshalb ist es so viel schneller.

Weil Sie wissenschaftlichen Code erwähnen, schauen Sie sich numpy . Was du getan hast, ist wohl schon schon gemacht (oder besser gesagt, es benutzt LAPACK für Sachen wie SVD). Wenn Sie hören, dass Python für wissenschaftlichen Code verwendet wird, sind die Menschen wahrscheinlich nicht darauf bedacht, es in der Art, wie Sie in Ihrem Beispiel zu verwenden.

Als kurzes Beispiel:

(Wenn Sie python3 verwenden, würde Ihr Beispiel die Float-Division verwenden. Mein Beispiel setzt voraus, dass Sie python2.x und damit eine Ganzzahl-Division verwenden. Wenn nicht, geben Sie i = np.arange(9999, dtype=np.float) , etc)

 import numpy as np i = np.arange(9999) j = np.arange(1, 9999) print np.divide.outer(i,j).sum() 

Um eine Vorstellung von Timing zu geben … (ich verwende hier eine Gleitkomma-Division, anstelle von Integer-Division wie in deinem Beispiel):

 import numpy as np def f1(num): total = 0.0 for i in range(num): for j in range(1, num): total += (float(i) / j) return total def f2(num): i = np.arange(num, dtype=np.float) j = np.arange(1, num, dtype=np.float) return np.divide.outer(i, j).sum() def f3(num): """Less memory-hungry (and faster) version of f2.""" total = 0.0 j = np.arange(1, num, dtype=np.float) for i in xrange(num): total += (i / j).sum() return total 

Wenn wir Timings vergleichen:

 In [30]: %timeit f1(9999) 1 loops, best of 3: 27.2 s per loop In [31]: %timeit f2(9999) 1 loops, best of 3: 1.46 s per loop In [32]: %timeit f3(9999) 1 loops, best of 3: 915 ms per loop 

Dies ist ein bekanntes Phänomen – Python-Code ist dynamisch und interpretiert, Java-Code ist statisch typisiert und kompiliert. Keine Überraschungen dort

Die Gründe, die Menschen für die Bevorzugung von Python geben, sind oft:

  • Kleinere Codebasis
  • Weniger Redundanz (mehr TROCKEN)
  • Sauberer Code

Allerdings, wenn Sie eine Bibliothek in C (von Python) geschrieben, die Leistung kann viel besser sein (vergleichen: cpickle to cpickle ).

Sie werden feststellen, dass Listenverfassungen oder Generatorausdrücke wesentlich schneller sind. Beispielsweise:

 total = sum(i / j for j in xrange(1, 9999) for i in xrange(9999)) 

Dies führt in ~ 11 Sekunden auf meinem Gerät vs ~ 26 für Ihren ursprünglichen Code. Immer noch eine Größenordnung langsamer als die Java, aber das ist mehr im Einklang mit dem, was man erwarten würde.

Ihr ursprünglicher Code kann übrigens leicht durch die Initialisierung der total auf 0 anstatt 0.0 beschleunigt werden, um Integer anstelle von Gleitkomma-Addition zu verwenden. Ihre Divisionen haben alle Integer-Ergebnisse, so dass es keinen Sinn gibt, die Ergebnisse zu einem Float zu summieren.

Auf meiner Maschine verlangsamt Psyco tatsächlich die Generatorausdrücke auf ungefähr die gleiche Geschwindigkeit wie deine ursprüngliche Schleife (die es überhaupt nicht beschleunigt).

Mit Artall's Listenverständnis

 total = sum(i / j for j in xrange(1, 9999) for i in xrange(9999)) 

Ist 10,2 Sekunden und mit pypy 1.7 ist es 2,5 Sekunden. Es ist lustig, weil Pypy die Originalversion auf 2,5 Sekunden beschleunigt. Also für pypy Liste Verständnis wäre vorzeitige Optimierung;). Gute Arbeit pypy!

Der Vorteil von Python ist, dass es viel mehr Flexibilität gibt (zB Klassen sind Objekte) im Vergleich zu Java (wo man nur diesen Reflexionsmechanismus hat)

Was hier nicht erwähnt wird, ist Cython . Es erlaubt, typisierte Variablen einzuführen und Ihr Beispiel zu C / C ++ zu kompilieren. Dann ist es viel schneller. Ich habe auch die Grenzen in der Schleife verändert …

 from __future__ import division cdef double total = 0.00 cdef int i, j for i in range(9999): for j in range(1, 10000+i): total += (i / j) from time import time t = time() print("total = %d" % total) print("time = %f[s]" % (time() - t)) 

gefolgt von

 $ cython loops.pyx $ gcc -I/usr/include/python2.7 -shared -pthread -fPIC -fwrapv -Wall -fno-strict-aliasing -O3 -o loops.so loops.c $ python -c "import loops" 

Gibt

 total = 514219068 time = 0.000047[s] 

Python für Loops werden statisch typisiert und interpretiert. Nicht kompiliert Java ist schneller, weil es zusätzliche JIT-Beschleunigungsfunktionen hat, die Python nicht hat.

http://de.wikipedia.org/wiki/Just-in-time_compilation

Um zu veranschaulichen, wie massiv ein Unterschied Java JIT macht, schau dir dieses Python-Programm an, das ca. 5 Minuten dauert:

 if __name__ =='__main__': total = 0.0 i=1 while i<=9999: j=1 while j<=9999: total=1 j+=1 i+=1 print total 

Während dieses grundsätzlich gleichwertige Java-Programm etwa 23 Millisekunden dauert:

 public class Main{ public static void main(String args[]){ float total = 0f; long start_time = System.nanoTime(); int i=1; while (i<=9999){ int j=1; while(j<=9999){ total+=1; j+=1; } i+=1; } long end_time = System.nanoTime(); System.out.println("total: " + total); System.out.println("total milliseconds: " + (end_time - start_time)/1000000); } } 

In Bezug auf alles in einer for-Schleife, Java reinigt python's Uhr, indem sie zwischen 1 und 1000 Größenordnungen schneller.

Moral der Geschichte: Grundpython für Loops sollte um jeden Preis vermieden werden, wenn eine schnelle Leistung erforderlich ist. Dies könnte daran liegen, dass Guido van Rossum die Menschen dazu ermutigt, Multiprozessor-freundliche Konstrukte wie Array-Splicing zu verwenden, die schneller als Java arbeiten.

Wenn man wissenschaftliche Berechnungen mit Python ausführt, dann ist es oftmals notwendig, eine in C / C ++ geschriebene Berechnungssoftware in den wichtigsten Teilen zu verwenden, mit Python als interne Skriptsprache, wie ex Sage (die auch viel Python-Code enthält).

Ich denke, dass dies nützlich sein kann: http://blog.dhananjaynene.com/2008/07/performance-comparison-c-java-python-ruby-jython-jruby-groovy/

Wie Sie sehen können, kann psyco / PyPy eine gewisse Verbesserung bringen, aber trotzdem wäre es wahrscheinlich viel langsamer als C ++ oder Java.

Nicht sicher, ob die Empfehlung gemacht worden ist, aber ich mag es, für Loops mit Listenverständnis zu ersetzen. Es ist schneller, sauberer und pythonischer.

http://www.pythonforbeginners.com/basics/list-comprehensions-in-python

  • Numba-Code langsamer als reine Pythonschlange
  • K-größte doppelte Auswahl
  • Steigerung der Leistung beim Erstellen von .PNG-Bildern in Python aus MatLab-Daten
  • Leistung von numpy.random.choice
  • Speedup pydev Debugging auf Python 2.6+
  • Warum ist Looping über Bereich () in Python schneller als mit einer while-Schleife?
  • Python-Funktion verlangsamt sich mit Vorhandensein großer Liste
  • Mit pyglet in python, warum ist meine Bildrate beschleunigen, wenn ich Maus ziehen?
  • Unterschied zur Leistung zwischen numpy und matlab
  • Link ATLAS / MKL zu einem installierten Numpy
  • Zähle, wie viele Elemente in einem numpy Array innerhalb des Deltas jedes anderen Elements sind
  • Python ist die beste Programmiersprache der Welt.