関数呼び出しやループのような単純なもの、そしてカウンターをインクリメントするループだけでもfarより多くの時間がPython =およびRubyチキンスキーム、ラケット、またはSBCLよりも。
これはなぜですか?スローネスは動的言語に支払う代償であると人々がよく言うのをよく耳にしますが、Lispは非常に動的であり、途方もなく遅いわけではありません(通常、Cの5倍未満の速度です; Ruby and = Pythonは2桁になる場合があります。さらに、LISPスタイルは再帰を使用しますが、常に末尾再帰ではありませんが、スタックはヒープ内の継続のリンクリストなどであり、命令型のPythonやRubyよりもLISPの速度を遅くする必要があります。
ラケットとSBCLはJITされていますが、チキンスキームは静的にコンパイルされるか、最適化されていないインタープリターを使用します。どちらも動的言語にあまり適しておらず、低速です。まだChicken Schemeの単純なcsi
インタープリターを使用していても(バイトコードのコンパイルも実行しません!)、PythonおよびRubyをはるかに超える速度が得られます。
なぜPythonとRuby)は、同様に動的なLispに比べて途方もなく遅いのですか?それは、オブジェクト指向であり、巨大なvtableと型階層が必要なためですか?
例:階乗関数。 Python:
def factorial(n):
if n == 0:
return 1
else:
return n*factorial(n-1)
for x in xrange(10000000):
i = factorial(10)
ラケット:
#lang racket
(define (factorial n)
(cond
[(zero? n) 1]
[else (* n (factorial (sub1 n)))]))
(define q 0)
(for ([i 10000000])
(set! q (factorial 10)))
タイミング結果:
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
コンパイルされたLISPシステムは、通常、RubyまたはPythonよりもかなり高速です。
たとえば、RubyとSBCLの比較を参照してください。
http://benchmarksgame.alioth.debian.org/u32/benchmark.php?test=all&lang=yarv&lang2=sbcl&data=u32
またはPythonおよびSBCL:
http://benchmarksgame.alioth.debian.org/u32/benchmark.php?test=all&lang=python3&lang2=sbcl&data=u32
ただし、次の点に注意してください。
また、一部の操作は類似しているように見えても、異なる場合があります。整数変数を反復するfor
ループは、範囲を反復するfor
ループと本当に同じですか?
Ruby/Python/etcでのメソッドディスパッチは負荷が高く、Ruby/Python/etcプログラムは主にメソッドを呼び出すことによって計算します。 Ruby内のfor
ループも、each
へのメソッド呼び出しの構文糖衣です。
私はあなたのラケットのインストールについては知りませんが、フラグなしで実行された場合、私がちょうどapt-get install
'したラケットはJITコンパイルを使用します。 --no-jit
で実行すると、Python時間(racket
:3s、racket --no-jit
:37s、python
: 74s)。また、モジュールのスコープでの割り当ては、Pythonでのローカルの割り当てよりも言語設計上の理由から(非常に自由度の高いモジュールシステム))、コードを関数に移動するとPython 60年代。残りのギャップは、偶然の一致、さまざまな最適化の焦点の組み合わせとして説明できます(関数呼び出しはLISPで非常に高速でなければならない、Python人々はあまり気にしない)、品質それぞれの言語設計の基本的な結果ではなく、実装(参照カウントと適切なGC、スタックVMとレジスタVM)など)。