Article View: comp.lang.functional
Article #640Re: The speed of various functional languages
From: Thomas M. Breuel
Date: Sun, 04 Oct 1992 22:09
Date: Sun, 04 Oct 1992 22:09
181 lines
7860 bytes
7860 bytes
In article <91544@netnews.upenn.edu> freeman@gradient.cis.upenn.edu (Jon Freeman) writes:
This is not a question that I would normally ask, but unfortunately I
need to ask it and I need answers as soon as possible...
In the personal experience of people on the net, which widely
available implementation of a functional language is the fastest on
comparable programs? I'm thinking about language-implementation pairs
like MIT Scheme, Lucid Common Lisp, Franz Lisp, etc.
Here's why I'm asking: I have been writing search algorithms for the
CNF-satisfiability problem in Standard ML of New Jersey, version 0.75.
Although I like SML very much, and I think its static type-checking is
fantastic for program development, I'm afraid I have to admit that its
performance on my algorithms, at least, is simply terrible.
You should do some simple benchmarks for yourself that test those
aspects of the implementation that you are interested in. NJ/SML does
not perform very well for arithmetic (in particular, floating point),
buts I believe that its garbage collector is generally faster than
that of the other languages. For non-numerical problems, I have not
noticed a big difference between NJ/SML and CommonLisp.
First, let me illustrate this claim with a specific example. There's
a local search algorithm for CNF-satisfiability called GSAT. The most
recent paper on GSAT is available via anonymous ftp from
research.att.com in the directory /dist/ai. Like all local search
algorithms, GSAT is very simple: it's essentially two nested FOR
loops. According to the above paper, the authors' implementation of
GSAT (written in C) can solve the 8, 20, and 30-queens problems in
0.1, 0.9, and 2.5 seconds, respectively. My SML implementation of
GSAT, on the other hand, requires an average of 3, 70, and 500 seconds
of CPU time, respectively. GSAT's simplicity makes these results
particularly troubling. My _backtracking_ search algorithms are also
much slower than their counterparts in the literature.
One problem you may be having is that the SML code you have written
is written less efficiently. In different words, you can write more
C-style code in SML, but, unlike C, SML gives you the choice of more
convenient, but slower, constructs. In my experience, a factor of 30
sounds like too big of a difference even between a C version and an
SML version of the same algorithm to be explicable by an inefficient
implementation of the language.
One particular stylistic choice, which will most likely cost you a
factor of 2-3 in execution speed, is currying (I find that it makes
your programs not only slower, but also harder to read and debug, but
that's a different issue...). Another important difference is that C
code is unsafe, while SML code is safe. You will find that C code
becomes significantly slower once you start adding all those safety
checks.
Thomas.
PS: Just FYI, here is a _very simple_ comparison of tupled function
calls and curried function calls in NJ/SML, and of the same program in
CMU-CL. Note that the CL version doesn't perform significantly
differently from the SML version. Also note that there are some flags
in NJ/SML analogous to "speed 3" that you can twiddle, but I didn't
bother. Type declarations in CL are, of course, a big mess...
================================================================
SML VERSION
================================================================
(* fold function "f" over the sublists of "(x::xs)" of size "n", starting
with value "r", and appending "p" to each sublist *)
fun folds_onto([]:'a list,1,r:'b,p:'a list,f:'b * 'a list -> 'b):'b = r
| folds_onto(x::xs,1,r,p,f) = folds_onto(xs,1,f(r,x::p),p,f)
| folds_onto([],n,r,p,f) = r
| folds_onto(x::xs,n,r,p,f) = folds_onto(xs,n,folds_onto(xs,n-1,r,x::p,f),p,f);
fun folds_onto' ([]:'a list) 1 (r:'b) (p:'a list) (f:'b -> 'a list -> 'b):'b = r
| folds_onto' (x::xs) 1 r p f = folds_onto' xs 1 (f r (x::p)) p f
| folds_onto' [] n r p f = r
| folds_onto' (x::xs) n r p f = folds_onto' xs n (folds_onto' xs (n-1) r (x::p) f) p f;
val l = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
val n = 10;
fun doit() = folds_onto(l,n,0,[],fn (total,l) => total+length(l));
fun doit'() = folds_onto' l n 0 [] (fn total => fn l => total+length l);
================================================================
IDIAP Standard Library - working version
eXene - working version
Concurrent ML, version 0.9.6, October 11, 1991
Standard ML of New Jersey, Version 0.84, July 5, 1992
val it = () : unit
[closing dump-elib]
val it = () : unit
-
val folds_onto = fn : 'a list * int * 'b * 'a list * ('b * 'a list -> 'b) -> 'b
val folds_onto' = fn : 'a list -> int -> 'b -> 'a list -> ('b -> 'a list -> 'b) -> 'b
val l = [1,2,3,4,5,6,7,8,9,10,11,12,...] : int list
val n = 10 : int
val doit = fn : unit -> int
val doit' = fn : unit -> int
[closing bar.sml]
val it = () : unit
compiled as above:
- timeit(doit);
user: 4.780000 gc: 0.010000 system: 0.040000 real: 4.950240
val it = 1847560 : int
- timeit(doit');
user: 15.970000 gc: 0.010000 system: 0.030000 real: 16.148106
val it = 1847560 : int
-
compiled as a single compilation unit (without the ";"s above):
- timeit(doit);
user: 3.120000 gc: 0.020000 system: 0.0 real: 3.152852
val it = 1847560 : int
- timeit(doit');
user: 7.880000 gc: 0.020000 system: 0.0 real: 7.925187
val it = 1847560 : int
-
================================================================
COMMON LISP VERSION
================================================================
(proclaim '(optimize (speed 3) (safety 0)))
(defconstant the-l '(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20))
(defconstant the-n 10)
(defun folds-onto (l n r p f)
(declare (fixnum n) (type (function (t t) t) f))
(if (null l)
r
(if (= n 1)
(folds-onto (cdr l) 1
(funcall f r (cons (car l) p))
p f)
(folds-onto (cdr l) n
(folds-onto (cdr l) (- n 1) r (cons (car l) p) f)
p f))))
(defun doit ()
(folds-onto the-l the-n 0 '()
#'(lambda (total l)
(declare (fixnum total) (list l))
(+ total (length l)))))
================================================================
CMU Common Lisp 16d, running on arolla
Send bug reports and questions to your local CMU CL maintainer, or to
cmucl-bugs@cs.cmu.edu.
Loaded subsystems:
Python 1.0, target SPARCstation/Sun 4
CLOS based on PCL version: 5/1/90 May Day PCL (REV 4b)
CLX X Library MIT R5.0
Hemlock 3.5
* ...
* (gc)
[GC threshold exceeded with 5,043,368 bytes in use. Commencing GC.]
[GC completed with 104,672 bytes retained and 4,938,696 bytes freed.]
[GC will next occur when at least 10,104,672 bytes are in use.]
NIL
* (time (doit))
Compiling LAMBDA NIL:
Compiling Top-Level Form:
Evaluation took:
3.51 seconds of real time
2.81 seconds of user run time
0.67 seconds of system run time
0 page faults and
4933328 bytes consed.
1847560
*
Technical Details
Message-ID:
<TMB.92Oct4180929@arolla.idiap.ch>Path:
arc1-ca.pugleaf.net!sparky!uunet!know!hri.com!spool.mu.edu!agate!ames!think.com!mintaka.lcs.mit.edu!ai-lab!life.ai.mit.edu!tmbReferences:
<91544@netnews.upenn.edu>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!sparky!uunet!know!hri.com!spool.mu.edu!agate!ames!think.com!mintaka.lcs.mit.edu!ai-lab!life.ai.mit.edu!tmb From: tmb@arolla.idiap.ch (Thomas M. Breuel) Newsgroups: comp.lang.functional Subject: Re: The speed of various functional languages Message-ID: <TMB.92Oct4180929@arolla.idiap.ch> Date: 4 Oct 92 22:09:29 GMT References: <91544@netnews.upenn.edu> Sender: news@ai.mit.edu Reply-To: tmb@idiap.ch Followup-To: comp.lang.functional Organization: IDIAP (Institut Dalle Molle d'Intelligence Artificielle Perceptive) Lines: 181 In-reply-to: freeman@gradient.cis.upenn.edu's message of 3 Oct 92 18:48:10 GMT