为什么Python和Ruby这么慢,而Lisp实现速度很快?

时间:2013-11-09 14:46:41

标签: python ruby performance scheme lisp

我发现像函数调用和循环这样的简单事情,甚至只是循环增加一个计数器,在Python和Ruby中的时间比多于鸡计划,球拍或SBCL。

为什么会这样?我经常听到有人说慢速是你为动态语言付出的代价,但是Lisps非常动态并且速度不是很慢(它们通常比C慢不到5倍; Ruby和Python可以达到两位数)。此外,Lisp样式使用递归,并不总是尾递归,很多,堆栈是堆中的连续列表等,这似乎是使Lisp比命令式样Python和Ruby慢的东西。 / p>

Racket和SBCL是JITted,但Chicken Scheme要么静态编译,要么使用非优化解释器,这两种解释器都应该非常适合动态语言而且速度慢。然而,即使使用朴素csi解释器进行Chicken Scheme(甚至不进行字节码编译!),我的速度远远超出了Python和Ruby。

与类似的动态Lisps相比,为什么Python和Ruby的速度如此之慢?是因为它们是面向对象的,需要巨大的vtable和类型heirarchies?

示例:阶乘函数。的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

4 个答案:

答案 0 :(得分:17)

编译的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

但请记住以下内容:

  • SBCL使用本机代码编译器。 使用字节代码机器或类似JIT编译器的字节代码到本机代码。在运行时之前,SBCL将源代码中的所有代码编译为本机代码。编译器是增量的,可以编译单个表达式。因此,EVAL函数和Read-Eval-Print-Loop也使用它。
  • SBCL使用优化编译器,它使用类型声明和类型推断。编译器生成本机代码。
  • Common Lisp允许各种优化,使代码不那么动态或不动态(内联,早期绑定,无类型检查,专用于声明类型的代码,尾调用优化,......)。使用这些高级功能的代码看起来很复杂 - 特别是当编译器需要被告知这些事情时。
  • 如果没有这些优化,编译Lisp代码仍然比解释代码更快,但比优化的编译代码慢。
  • Common Lisp提供了CLOS,即Common Lisp对象系统。 CLOS代码通常比非CLOS慢 - 这种比较有意义。动态函数式语言往往比动态面向对象语言更快。
  • 如果语言实现使用高度优化的运行时,例如对于bignum算术运算,则慢速语言实现可能比优化编译器更快。有些语言在C中实现了许多复杂的原语。这些语言往往很快,而其他语言可能会很慢。

此外,某些操作可能看起来相似,但可能会有所不同。迭代整数变量的for循环是否与迭代范围的for循环完全相同?

答案 1 :(得分:12)

Ruby / Python / etc中的方法调度很昂贵,而Ruby / Python / etc程序主要通过调用方法来计算。甚至Ruby中的for循环只是对each的方法调用的语法糖。

答案 2 :(得分:2)

我不知道您的球拍安装,但是如果没有标志运行,我只是apt-get install使用JIT编译。使用--no-jit运行的时间更接近Python时间(racket:3s,racket --no-jit:37s,python:74s)。此外,出于语言设计原因,模块范围内的赋值比Python中的局部赋值慢(非常自由的模块系统),将代码移动到函数中将Python放在60s。剩余的差距可能可以解释为巧合的一些组合,不同的优化焦点(函数调用必须在Lisp中快速疯狂,Python人更少关心),实现质量(ref-counting与正确的GC,堆栈VM与寄存器VM)等等,而不是语言设计的基本结果。

答案 3 :(得分:-4)

我认为不是Python本身就很慢,而是Python解释器以较慢的速度移动代码。如果您尝试使用py2exe等工具编译代码,那么它可能比lisp更快。你必须尝试一下,但我认为它只是一个缓慢的翻译。