Warum Python und Ruby so langsam, während das Lisp-Implementierungen sind schnell?
Finde ich, dass einfache Dinge, wie Funktionsaufrufe und Schleifen, und Schleifen Inkrementieren einen Zähler nehmen weit mehr Zeit in Python und Ruby als im Chicken Scheme, Racket, oder SBCL.
Warum ist das so? Oft höre ich Leute sagen, dass die Langsamkeit ist ein Preis, den Sie bezahlen für dynamische Sprachen, aber Lisps sind sehr dynamisch und sind nicht lächerlich langsam (Sie sind in der Regel weniger als 5 mal langsamer als C, Ruby und Python kann in den zweistelligen Bereich). Außerdem Lisp-Stil verwendet Rekursion, und nicht immer tail-Rekursion, einer viel, der stack ist eine verknüpfte Liste von Fortsetzungen in den heap, etc, die scheinen, zu sein die Dinge machen sollten Lisp langsamer als der Imperativ-Stil, Python und Ruby.
Schläger und SBCL sind JITted, aber Chicken Scheme ist entweder statisch kompiliert werden, oder verwendet ein nicht-optimierender interpreter, beide sollten schlecht geeignet, um dynamische Sprachen und langsam. Doch auch mit der naiven csi
Dolmetscher für Hähnchen-System (das gar nicht tun bytecode-Kompilierung!), Bekomme ich Geschwindigkeiten von weit über Python und Ruby.
Warum genau sind Python und Ruby so lächerlich langsam im Vergleich zu den ähnlich dynamische Lisps? Ist es, weil Sie Objekt-orientiert und brauchen riesige vtables und geben heirarchies?
Beispiel: Fakultät-Funktion. Python:
def factorial(n):
if n == 0:
return 1
else:
return n*factorial(n-1)
for x in xrange(10000000):
i = factorial(10)
Schläger:
#lang racket
(define (factorial n)
(cond
[(zero? n) 1]
[else (* n (factorial (sub1 n)))]))
(define q 0)
(for ([i 10000000])
(set! q (factorial 10)))
Timing Resultate:
ithisa@miyasa /scratch> time racket factorial.rkt
racket factorial.rkt 1.00s user 0.03s system 99% cpu 1.032 total
ithisa@miyasa /scratch> time python factorial.py
python factorial.py 13.66s user 0.01s system 100% cpu 13.653 total
- SBCL nicht eine Verwendung eines JIT. Es ist strengstens AOT.
- SBCL ist eine REPL-interpreter...mir ist das common lisp n00b immer als Dolmetscher...
- SBCL verwendet einen compiler. Immer und überall. In der REPL auch. SBCL verwendet keinen interpreter, standardmäßig. Jeden Ausdruck, den Sie eingeben, an der REPL kompiliert wird, bevor er ausgeführt wird.
- Ja, so REPL ist technisch ein JIT-compiler...
- Nein. Die REPL ruft einfach nur EVAL, die ruft die native-code-compiler. Der code vollständig kompiliert aus dem source-code in systemeigenen code VOR der Laufzeit. Es ist nur eine inkrementelle compiler, die kompilieren können, den individuellen Ausdruck. Es gibt keine byte code compilation, keine byte-code in systemeigenen code JIT-Kompilierung, keine runtime-Analyse keine code-cache, ... Es ist eine inkrementelle native-code-compiler.
- Weil die Menschen damit verbracht haben, mit 55 Jahren, die die Lisp-schnell, aber nur bei 20,5 Jahren, die die Ruby schnell. Und weil die Menschen damit verbracht haben, Millionen von Dollar zu machen Lisp schnell.
- Lisps sind nicht dynamisch, im Vergleich mit der Ente-typisierten OO-Sprachen.
- In welcher Weise ist Lisp "nicht dynamisch"?
- Lisp verlassen Sie sich nicht auf dynamische Methode dispatch. Es hat einige polymorphe Funktionen (notorisch, numerische Turm), aber die Mehrheit der call-Ziele sind entscheidbar, im compile-Zeit, im Gegensatz zu Sprachen wie Python, wo jeder Anruf muss entschieden werden, in der runtime.
- Der Maßstab nicht gerecht wird einem erfahrenen python-Nutzer Ausgabe. Sie würden mit pypy, numba, oder cython, wenn der code nicht schnell genug. Ich habe 18 Sekunden für den code, so sah cython in meinem jupyter notebook. Eine naive version beschleunigt es bis um 4 mal um 5 Sekunden. Eine differenziertere Anwendung reduziert Sie 0,33 Sekunden schneller als lisp und oft nähert sich C. nbviewer.jupyter.org/github/john9631/Jupyter-Notebooks/blob/...
Du musst angemeldet sein, um einen Kommentar abzugeben.
Kompilierte Lisp-Systeme sind in der Regel um einiges schneller als Ruby oder Python.
Siehe zum Beispiel den Vergleich von Ruby und SBCL:
http://benchmarksgame.alioth.debian.org/u32/benchmark.php?test=all&lang=yarv&lang2=sbcl&data=u32
oder Python und SBCL:
http://benchmarksgame.alioth.debian.org/u32/benchmark.php?test=all&lang=python3&lang2=sbcl&data=u32
Aber Bedenken Sie Folgendes:
Auch einige Vorgänge können ähnlich Aussehen, aber anders sein könnte. Ist ein
for
Schleife iteriert eine integer-variable wirklich das gleiche wie einfor
- Schleife iteriert über einen Bereich?Methode dispatch in Ruby/Python/etc ist teuer, und Ruby/Python/etc-Programme berechnen Sie in Erster Linie durch das aufrufen von Methoden. Auch
for
Schleifen in Ruby sind nur syntaktischer Zucker für einen Methodenaufruf aneach
.Ich weiß nicht, über Ihr Schläger-installation, aber die Schläger, die ich gerade
apt-get install
'd verwendet die JIT-Kompilierung, wenn ausgeführt wird, ohne Fahnen. Läuft mit--no-jit
gibt eine Zeit, die sehr viel näher an der Python-Zeit (racket
: 3s,racket --no-jit
: 37s,python
: 74s). Auch die Zuordnung zu Modul Umfang ist langsamer als eine lokale Zuordnung in Python für Sprache, design-Gründen (sehr liberal Modul-system), verschieben Sie den code in eine Funktion setzt Python 60er Jahre. Die Verbleibende Lücke kann wahrscheinlich erklärt werden, wie eine Kombination von Zufall, verschiedene Optimierungs-Fokus (Funktionsaufrufe müssen verrückt sein, schnell in Lisp, Python-Leute kümmern sich weniger), die Qualität der Umsetzung (ref-counting-versus ordnungsgemäße GC, stack-VM versus register VM), etc. anstatt eine fundamentale Konsequenz der jeweiligen Sprache-designs.--no-jit
keinen Unterschied; obwohl zugegebenermaßen die meisten I/O gebunden.return
s von Funktionen und Sie brauchen, um zu schmücken, die Funktion von frames als den Punkt zurück, usw. Ob solche "features" die Kosten Wert sind oder nicht, ist eine subjektive Debatte.**kwds
ist eine wahrscheinlichere Quelle der Langsamkeit.Ich denke, dass eher als Python wird langsam selbst, dass es der Python-interpreter sich durch den code in einem langsameren Tempo. Wenn Sie versucht haben, kompilieren Sie den code mit einem tool wie py2exe, dann kann es schneller sein als lisp. Sie würde es versuchen, aber ich glaube, es ist ein langsamer interpreter.
q += factorial(10)
undprint q
am Endeexec(open(module).read())
. Versuchen Cython oder Nuitka statt zu Messen, die interpretation overhead, diese kompilieren von Python(-ish) - code C-API-Aufrufe.