Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are Python and Ruby so slow, while Lisp implementations are fast?

I find that simple things like function calls and loops, and even just loops incrementing a counter take far more time in Python and Ruby than in Chicken Scheme, Racket, or SBCL.

Why is this so? I often hear people say that slowness is a price you pay for dynamic languages, but Lisps are very dynamic and are not ridiculously slow (they are usually less than 5 times slower than C; Ruby and Python can go into the double digits). Besides, Lisp style uses recursion, and not always tail recursion, a lot, the stack is a linked list of continuations in the heap, etc, which seem to be things that should make Lisp slower than the imperative-style Python and Ruby.

Racket and SBCL are JITted, but Chicken Scheme is either statically compiled, or uses a non-optimizing interpreter, both of which should be badly suited to dynamic languages and slow. Yet even using the naive csi interpreter for Chicken Scheme (which doesn't even do bytecode compilation!), I get speeds far beyond Python and Ruby.

Why exactly are Python and Ruby so ridiculously slow compared to the similarly dynamic Lisps? Is it because they are object oriented and need huge vtables and type heirarchies?

Example: factorial function. Python:

def factorial(n):     if n == 0:         return 1     else:     return n*factorial(n-1)  for x in xrange(10000000):     i = factorial(10) 

Racket:

#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 results:

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 
like image 394
ithisa Avatar asked Nov 09 '13 14:11

ithisa


People also ask

Is Common Lisp faster than Python?

The processing of Python Programming language is faster when compared to the processing in Lisp Programming language. Python programming language is the most suitable language to work on Artificial Intelligence when compared to Lisp Programming language.

Why is Lisp so fast?

It is an incremental native code compiler. Because people have spent 55 years making Lisp fast, but only 20.5 years making Ruby fast. And because people have spent millions of dollars making Lisp fast.

Is Lisp a fast language?

Contrary to popular belief, Lisp code can be very ef- ficient today: it can run as fast as equivalent C code or even faster in some cases.

Why Ruby is an acceptable Lisp?

Ruby is a denser functional language than LISP A dense language lets you say things concisely, without obfuscation. You can see more of your program in one glance, and there aren't as many places for bugs to hide. Beyond a certain point, the only way to make programs denser is to use more powerful abstractions.


2 Answers

Natively compiled Lisp systems are usually quite a bit faster than non-natively compiled Lisp, Ruby or Python implementations.

Definitions:

  • natively compiled -> compiles to machine code
  • compiled -> compiles to machine code or some other target (like byte code, JVM instructions, C code, ...)
  • interpreted Lisp -> runs s-expressions directly without compilation
  • interpreted Python -> runs compiled Python in a byte-code interpreter. The default Python implementation is not really interpreted, but using a compiler to a byte code instruction set. The byte code gets interpreted. Typically byte code interpreters are slower than execution of native code.

But keep in mind the following:

  • SBCL uses a native code compiler. It does not use a byte code machine or something like a JIT compiler from byte code to native code. SBCL compiles all code from source code to native code, before runtime. The compiler is incremental and can compile individual expressions. Thus it is used also by the EVAL function and from the Read-Eval-Print-Loop.
  • SBCL uses an optimizing compiler which makes use of type declarations and type inference. The compiler generates native code.
  • Common Lisp allows various optimizations which make the code less dynamic or not dynamic (inlining, early binding, no type checks, code specialized for declared types, tail-call optimizations, ...). Code which makes use of these advanced features can look complicated - especially when the compiler needs to be told about these things.
  • Without these optimizations compiled Lisp code is still faster than interpreted code, but slower than optimized compiled code.
  • Common Lisp provides CLOS, the Common Lisp Object System. CLOS code usually is slower than non-CLOS - where this comparison makes sense. A dynamic functional language tends to be faster than a dynamic object-oriented language.
  • If a language implementation uses a highly optimized runtime, for example for bignum arithmetic operations, a slow language implementation can be faster than an optimizing compiler. Some languages have many complex primitives implemented in C. Those tend to be fast, while the rest of the language can be very slow.
  • there can also be implementations of Python, which generate and run machine code, like the JIT compiler from PyPy. Ruby also now has a JIT compiler since Ruby 2.6.

Also some operations may look similar, but could be different. Is a for loop iterating over an integer variable really the same as a for loop which iterates over a range?

like image 162
Rainer Joswig Avatar answered Oct 08 '22 19:10

Rainer Joswig


Method dispatch in Ruby/Python/etc is expensive, and Ruby/Python/etc programs compute primarily by calling methods. Even for loops in Ruby are just syntactic sugar for a method call to each.

like image 29
Alex D Avatar answered Oct 08 '22 17:10

Alex D