LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
How to Make LISP Go Faster than C
Didier Verna
didier@lrde.epita.fr
http://www.lrde.epita.fr/˜didier
Version 1.4 – June 13, 2006
1/24
LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
Introduction
Myths and legends. . .
ā€LISP is slowā€ . . . NOT ! (it’s been 20 years)
Why is LISP fast ?

Smart compilers (⇒ native machine code)

Static typing (types known at compile-time)

Safety levels (compiler optimizations)

Efficient data structures (arrays, hash tables etc.)
Demonstration:

Comparative C and LISP benchmarks

4 simple image processing algorithms

Pixel storage and access / arithmetic operations
⇒ Equivalent performance
(LISP 10% better in some cases)
2/24
LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
Table of contents
1 Experimental Conditions
2 C Programs and Benchmarks
3 LISP code, take 1
4 LISP code, take 2
Typing mechanisms
Optimization
Results
5 Type inference
3/24
LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
Experimental conditions
The algorithms: the ā€œpoint-wiseā€ class

Pixel assignment / addition / multiplication / division

Parameters: image size / type / storage

Presented: 800 āˆ— 800 int / float images
The protocol

Debian GNU Linux / 2.4.27-2-686 packaged kernel

Pentium 4 3GHz / 1GB RAM / 1MB level 2 cache

Single user mode / SMP off (no hyperthreading)

Measures on 200 consecutive iterations
5/24
LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
C code sample
The add function
void add ( image āˆ— to , image āˆ— from , f lo a t v a l )
{
i n t i ;
const in t n = imaāˆ’>n ;
for ( i = 0 ; i < n ; ++ i )
toāˆ’>data [ i ] = fromāˆ’>data [ i ] + val ;
}
Gcc 4.0.3 (Debian package)
Full optimization: -O3 -DNDEBUG plus inlining
Note: inlining should be almost negligible
7/24
LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
Results (seconds)
Time is of the Essence
Fully optimized inlined C code
Algorithm Integer Image Float Image
Assignment 0.29 0.29
Addition 0.48 0.47
Multiplication 0.48 0.46
Division 0.58 1.93
Surprise: integer division should be costly
ā€œConstant Integer Optimizationā€ (with inlining)
Do not neglect inlining !
8/24
LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
First shot at LISP code
CMU-CL 19c (CVS)
The add function, take 1
( defun add ( t o from v al )
( l e t ( ( s i z e ( arrayāˆ’dimension to 0 ) ) )
( dotimes ( i s i z e )
( s etf ( a r e f to i ) (+ ( aref from i ) val ) ) ) ) )
COMMO N-LISP’s standard simple-array type
Interpreted version: 2300x
Compiled version: 60x
Optimized version: 20x
Untyped code ⇒ dynamic type checking !
10/24
LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
Typing mechanisms
Typing paradigm:

Type information (COMMON-LISP standard)
Declare the expected types of LISP objects

Type information is optional
Declare only what you know; give hints to the compilers

Both a statically and dynamically typed language
Typing mechanisms:

Function arguments:
(make-array size :element-type ’single-float)

Type declarations:
Function parameter / freshly bound local variable

. . .
12/24
LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
Typed LISP code sample
Declaring the types of function parameters
The add function, take 2
( defun add ( t o from v al )
( d e c l a r e ( type ( simpleāˆ’array s i n g l e āˆ’f l o a t ( āˆ— ) )
t o from ) )
( d e c l a r e ( type s i n gl e āˆ’ f l o a t v a l ) )
( l e t ( ( s i z e ( arrayāˆ’dimension to 0 ) ) )
( dotimes ( i s i z e )
( s etf ( a r e f to i ) (+ ( aref from i ) val ) ) ) ) )
simple-array’s . . .
of single-float’s . . .
unidimensional.
13/24
LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
Object representation
Why typing matters for performance
Dynamic typing ⇒ objects of any type (worse: any size)
LISP variables don’t carry type information: objects do
The ā€œboxedā€ representation of LISP objects
Type information
Pointer to Lisp Object
Actual value
Dynamic type checking is costly !
Pointer dereferencing is costly !
14/24
LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
The benefits of typing
2 examples
Array storage layout:

Homogeneous arrays of a known type
⇒ native representation usable

Specialization of the aref function

ā€œOpen Codingā€
Immediate objects:

Short (less than a memory word)

Special ā€œtag bitsā€ (invalid as pointer values)

⇒ Encoded inline
Unboxed fixnum representation
00
0
1
Tag bits
Bits 1 ... 29 30 31 32
fixnum value (30 bits)
15/24
LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
Example: optimizing a loop index
(dotimes (i 100) ...)
Disassembly of a dotimes macro
58701478: .ENTRY FOO( )
90: POP DWORD PTR [ EBPāˆ’8]
93: LEA ESP, [ EBPāˆ’32]
96: XOR EAX, EAX
98: JMP L1
9A: L0 : ADD EAX, 4
9D: L1 : CMP EAX, 400
A2 : JL L0
A4 : MOV EDX, #x2800000B
A9 : MOV ECX, [ EBPāˆ’8]
AC: MOV EAX , [EBPāˆ’4]
AF: ADD ECX, 2
B2 : MOV ESP, EBP
B4 : MOV EBP, EAX
B6 : JMP ECX
16/24
LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
Activating optimization
ā€œQualitiesā€ (COMMON-LISP standard): between 0 and 3
safety, speed etc.
Global or local declarations in source code
(no compiler flag)
Global qualities declaration
(declaim (optimize (speed 3)
(compilation-speed 0)
(safety 0)
(debug 0)))
Safe code: declarations treated as assertions
Optimized code: declarations trusted
17/24
LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
Results
And here comes a little surprise. . .
C and LISP comparative performance
Integer Image Float Image
Algorithm
C LISP C LISP
Assignment 0.29 0.29 0.29 0.29
Addition 0.48 0.48 0.47 0.46
Multiplication 0.48 0.48 0.46 0.45
Division 0.58 1.80 1.93 1.72
Identical performances from C and LISP
C better at integer division
(no ā€œconstant integer optimizationā€ in LISP compilers)
Surprise: LISP 10% faster at floating-point division
18/24
LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
Type inference
Static typing is not as easy as it seems. . .
What to do when not all types are provided ?

What about the type of i and size ?

What about the type of (
*
fixnum fixnum) ?
⇒ Figure out at run-time

Stay dynamically typed

Use boxed representations
⇒ Infer the missing types . . . but

Type inference systems of various behavior and quality

COMMON-LISP standard too weak about type
declarations
20/24
LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
Example of type inference
multiply excerpt
; ; . . .
( d e c l a r e ( type ( simpleāˆ’array fixnum ( āˆ— ) ) to from ) )
( d e c l a r e ( type f ixnum v al ) )
; ; . . .
( s etf ( a r e f to i ) ( the fixnum (āˆ— ( a r e f from i ) va l ) ) ) ) ) )
(
*
fixnum fixnum) = fixnum in general . . . but

to declared as an array of fixnum’s

So the multiplication has to return a fixnum
Sadly, not all type inference systems are that smart
(e.g. Allegro)

Need for further explicit type information

Type declarations for intermediate values: the
21/24
LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
Conclusion
Optimizing LISP code:
data structures, type declarations, optimization
Today’s compilers are smart:
performance can be equivalent to (or better than) C
Typing can be cumbersome
(source code annotation)
Difficult to provide both correct and minimal information
(weakness of the COMMON-LISP standard)
22/24
LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
Perspectives
Low level: try other compilers / architectures
(and compiler / architecture specific optimization
settings)
Medium level: try more sophisticated algorithms
(neighborhoods, front-propagation)
High level: try different levels of genericity
(dynamic object orientation, static meta-programming)
Do not restrict to image processing
23/24
LISP:
faster than C?
Didier Verna
Introduction
Experiments
The case of C
Raw LISP
Typed LISP
Typing mechanisms
Optimization
Results
Type inference
Conclusion
In greater detail. . .
For the interested reader
Beating C in Scientific Computing Applications
On the Behavior and Performance of Lisp, Part I.
Verna, D. (2006). In Third European LISP Workshop at
ECOOP, Nantes, France.
http://lisp-ecoop06.bknr.net/.
Logo by Manfred Spiller
24/24