One of the issues that has come up in discussions of Lisp vs.
mainstream languages, is the quality of the compiler. I've
done some testing on various problems, and here is my
conclusion about the current state of a few compilers, with
respect to something that one other language's compiler does
OK on. Floating point.
The fortran (f77) compiler, with default settings on SPARC1+
computers produces code that runs about as fast as the
Allegro CL 4.1 compiler, with "optimize speed (safety 0)" and careful
declarations.
The fortran compiler with full optimization (-fast -O3 options)
produces code that runs about 10 times faster than either of the previously
indicated situations.
I would be delighted to hear other peoples' results.
(Though if all you are comparing is the same 2 pieces of software on the
same hardware, I guess I would be bored :)).
For reference, here are the 2 benchmarks.
Here is a fortran program that on a SPARC1+
runs a 100x100 by 100x100 matrix mult in 4.9 seconds; 0.48 seconds with
high optimization.
c main program/ driver
dimension u(100,100),v(100,100),w(100,100),tarr(2)
c set up test matrices
do 10 i=1,100
do 10 j=1,100
v(i,j)=1.0
10 u(i,j)=1.0
c initialize timer
start=dtime(tarr)
call matmul(u,v,w,100,100,100)
elapsed=dtime(tarr)
c print user and system time in seconds
print 1, tarr(1),tarr(2)
1 format(f12.3)
end
c this is what we are comparing -- time for matmul
subroutine matmul(a,b,c,n,m,k)
dimension a(n,m),b(m,k),c(n,k)
do 30 i=1,n
do 30 j=1,k
sum=0.0
do 20 l=1,m
20 sum=sum+a(i,l)*b(l,j)
30 c(i,j)=sum
end
.............................
Here is the Lisp program I am comparing it with. It takes 5.1 seconds
to run, compiled in Allegro CL 4.1. (Note, I am told that Allegro 4.2
will do better on this.)
......................
;; -*- mode: common lisp; -*-
#| Experiments with matrix multiply in Lisp.
Compute C=A.B, matrix product
|#
(defun matmul(a b c n m k)
;; in Allegro 4.1 this allocates no new
;; floating point space, and therefore does no GC.
(declare (optimize speed (safety 0))
(type (simple-array single-float (* *)) a b c)
(fixnum n m k))
(let ((sum 0.0))
(declare (single-float sum))
(dotimes (i n c) ;run through columns; return c
(declare (fixnum i))
(dotimes (j k)
(declare (fixnum j))
(setf sum 0.0)
(dotimes (l m)
(declare (fixnum l))
(setf sum (+ sum (* (aref a i l)(aref b l j)))))
(setf (aref c i j) sum)))))
;;Some test data
(defparameter n0 100)
(defparameter m0 100)
(defparameter k0 100)
(defparameter a0
(make-array (list n0 m0) :element-type 'single-float :initial-element 1.0))
;;a0 is n columns, m rows
(defparameter b0
(make-array (list m0 k0) :element-type 'single-float :initial-element 2.0))
;;b0 is m columns k rows
(defparameter c0
(make-array (list n0 k0) :element-type 'single-float))
;; c0 is n columns k rows
#| to time this, compile the matmul, and
(time (matmul? a0 b0 c0 n0 m0 k0))
|#
.............
I will be glad to summarize info mailed to me.
--
Richard J. Fateman
·······@cs.berkeley.edu 510 642-1879
A while back there were some comments about Lisp being inefficient
in terms of I/O. Has anyone done any benchmarks to test this?
Perhaps reading and writing a million characters via read-char/
write-char vs. read-line/write-line, and comparing that to similar
functions in C and Fortran.
Bob
·······@cs.umass.edu
In article <·····@dime.cs.umass.edu> ·······@canberra.cs.umass.edu (Bob Krovetz) writes:
A while back there were some comments about Lisp being inefficient
in terms of I/O. Has anyone done any benchmarks to test this?
sure.
i just wrote a c program to read million characters using getchar. it
takes 1.7 seconds to do it.
my lisp version (compiled using cmu common lisp, default settings) of
the same thing took 0.4 seconds to read 10,000 characters, and i hope
it will be done reading the full million by the time i finish this
paragraph. ahhh... yes. 38.4 seconds. this was for the following
code:
(defvar buffer (make-array 10000 :element-type 'standard-char))
(defun test(s)
(dotimes (i 10000)
(setf (elt buffer i) (read-char s)))
buffer)
Perhaps reading and writing a million characters via read-char/
write-char vs. read-line/write-line, and comparing that to similar
functions in C and Fortran.
i am not interested in read-line since i usually am trying to read
binary files when reading very large files. no optimizations were
done. as a mild matter of interest, i converted the c code to use
fread instead of getchar. the time required dropped to 0.8 seconds.
i am happy to take a factor of 2-4 in return for what lisp gives me,
but a factor of 20-40 is a bit much. last time i tried doing this
sort of thing, i had to crib some of the system sources which had a
special macro-ized version of read-char since the portable version was
*so* slow.
this could probably be fixed by a few routines to
a) read a buffer full of somethings (like fread)
b) a *fast* (not read) and reliable way to shrinkwrap/unwrap data
elements from a chunk read as in a). this has no equivalent in
standard c, but is similar in intent to sun's xdr.
Date: Tue, 23 Feb 1993 01:44 EST
From: Ted Dunning <···@nmsu.edu>
In article <·····@dime.cs.umass.edu> ·······@canberra.cs.umass.edu (Bob Krovetz) writes:
A while back there were some comments about Lisp being inefficient
in terms of I/O. Has anyone done any benchmarks to test this?
sure.
i just wrote a c program to read million characters using getchar. it
takes 1.7 seconds to do it.
my lisp version (compiled using cmu common lisp, default settings) of
the same thing took 0.4 seconds to read 10,000 characters, and i hope
it will be done reading the full million by the time i finish this
paragraph. ahhh... yes. 38.4 seconds. this was for the following
code:
(defvar buffer (make-array 10000 :element-type 'standard-char))
(defun test(s)
(dotimes (i 10000)
(setf (elt buffer i) (read-char s)))
buffer)
Perhaps reading and writing a million characters via read-char/
write-char vs. read-line/write-line, and comparing that to similar
functions in C and Fortran.
i am not interested in read-line since i usually am trying to read
binary files when reading very large files. no optimizations were
done. as a mild matter of interest, i converted the c code to use
fread instead of getchar. the time required dropped to 0.8 seconds.
i am happy to take a factor of 2-4 in return for what lisp gives me,
but a factor of 20-40 is a bit much. last time i tried doing this
sort of thing, i had to crib some of the system sources which had a
special macro-ized version of read-char since the portable version was
*so* slow.
this could probably be fixed by a few routines to
a) read a buffer full of somethings (like fread)
b) a *fast* (not read) and reliable way to shrinkwrap/unwrap data
elements from a chunk read as in a). this has no equivalent in
standard c, but is similar in intent to sun's xdr.
The following form on a Symbolics XL1200 reading a file using TCP/FTP
from another Symbolics XL1200 (which are generally considered slow file
servers compared to Unix servers) took 0.55 seconds to read 100,000
bytes, which would be about 5.5 seconds to read 1,000,000 bytes. That's
about 3.5 times slower than the Unix version, which you said is
acceptable performance.
(defvar *bolix-buffer* (make-array 100000 :element-type 'standard-char))
(defun bolix-test (s)
(scl:send s :string-in nil *bolix-buffer* 0 100000)
*bolix-buffer*)
Much to my surprise, doing the same test using TCP/FTP to OSF/1 running
on a DEC Alpha, it took about 0.65 seconds. Using NFS to the same
machine took about 1.1 seconds, almost all of it actually waiting for
the data to get back to me from over the network. I guess this means
that the Alpha just can't crank the bytes to us very fast, despite the
fact that it's 10 times faster than an XL1200.
Doing the same test to a local file system took 0.11 seconds, meaning
that I could read 1,000,000 bytes in 1.1 seconds. That's about 1.5
times faster than your C program. (Of course, I have no idea what sort
of a machine you are using.)
What this means is, a little diligence implementing a file stream system
can yield excellent performance in Lisp. It's really too bad that X3J13
didn't address the issue of standardized stream interfaces. Most of the
Lisp vendors implement most of David Gray's stream proposal, so perhaps
it's time to agree on a good buffered I/O interface as well.
···@stony-brook.scrc.symbolics.com (Scott McKay) writes:
>The following form on a Symbolics XL1200 reading a file using TCP/FTP
>from another Symbolics XL1200 (which are generally considered slow file
>servers compared to Unix servers) took 0.55 seconds
> (defvar *bolix-buffer* (make-array 100000 :element-type 'standard-char))
> (defun bolix-test (s)
> (scl:send s :string-in nil *bolix-buffer* 0 100000)
> *bolix-buffer*)
>What this means is, a little diligence implementing a file stream system
>can yield excellent performance in Lisp.
This is silly. You are comparing a non-portable block-at-a-time operation
(SCL:SEND) to a portable, character-at-a-time operation (getchar).
Of _course_ the lisp is going to win.
Try comparing getchar to your lisp's portable character-at-a-time
operation (ie, READ-CHAR).
Date: Tue, 23 Feb 1993 12:40 EST
From: Joshua M Yelon <·······@ehsn11.cen.uiuc.edu>
···@stony-brook.scrc.symbolics.com (Scott McKay) writes:
>The following form on a Symbolics XL1200 reading a file using TCP/FTP
>from another Symbolics XL1200 (which are generally considered slow file
>servers compared to Unix servers) took 0.55 seconds
> (defvar *bolix-buffer* (make-array 100000 :element-type 'standard-char))
> (defun bolix-test (s)
> (scl:send s :string-in nil *bolix-buffer* 0 100000)
> *bolix-buffer*)
>What this means is, a little diligence implementing a file stream system
>can yield excellent performance in Lisp.
This is silly. You are comparing a non-portable block-at-a-time operation
(SCL:SEND) to a portable, character-at-a-time operation (getchar).
Of _course_ the lisp is going to win.
Try comparing getchar to your lisp's portable character-at-a-time
operation (ie, READ-CHAR).
Please re-read the last paragraph of my message, which I include here:
What this means is, a little diligence implementing a file stream system
can yield excellent performance in Lisp. It's really too bad that X3J13
didn't address the issue of standardized stream interfaces. Most of the
Lisp vendors implement most of David Gray's stream proposal, so perhaps
it's time to agree on a good buffered I/O interface as well.
The only thing that is silly is that there are no portable, buffered I/O
operations in Common Lisp, and the point of my message is that we should
address this deficiency in a principled, portable way.
In article <····················@SUMMER.SCRC.Symbolics.COM> ···@stony-brook.scrc.symbolics.com (Scott McKay) writes:
The only thing that is silly is that there are no portable, buffered I/O
operations in Common Lisp, and the point of my message is that we should
address this deficiency in a principled, portable way.
hopefully by buffered i/o operations, i hope you mean the equivalent
of getchar. if not, then there still isn't any hope.
and btw... i am curious how fast the C equivalent runs on the
symbolics.
that is, if it indeed does run on the symbolics.
It's just not that hard to do fast I/O in lisp. The following toy code
implements very simple buffered binary I/O (actually just input) with
performance equivalent to getchar in C. It's not pure portable Common Lisp,
but the amount of implementation-dependent code is minimal. I'll agree
that it would be nice if there were standard inferfaces for this sort
of thing; until then, I can live with such minor system dependencies.
This code is at the same level as getchar, so it's not surprising that its
performance is comparable to C code (within a few percent, give or take).
Reading 1 million chars takes about 0.7 sec on a sparc 10/30, about 4 sec
on a Symbolics UX1200S.
;;; -*- Mode: LISP; Syntax: Common-lisp; Package: FIO; Base: 10; -*-
(eval-when (compile load eval)
(defconstant *bufsiz* 4096))
(deftype ib-buf () `(simple-array (unsigned-byte 8) (,*bufsiz*)))
(defstruct ib
(stream nil)
(buf (make-array *bufsiz* :element-type '(unsigned-byte 8)
:initial-element 0 :adjustable nil)
:type ib-buf)
(nbytes 0 :type (integer 0 #.*bufsiz*))
(offset 0 :type (integer 0 (#.*bufsiz*))))
(defun open-ib (file &key direction)
(let ((stream (open file :direction direction
:element-type '(unsigned-byte 8))))
(make-ib :stream stream)))
(defun close-ib (ib)
(close (ib-stream ib)))
(defmacro %ib-read-byte (ib)
`(locally (declare (optimize (speed 3) (safety 1)))
(unless (< (ib-offset ,ib) (ib-nbytes ,ib))
(ib-fill-buffer ,ib))
(prog1
(aref (ib-buf ,ib) (ib-offset ,ib))
(incf (ib-offset ,ib)))))
(defun ib-fill-buffer (ib)
(setf (ib-nbytes ib)
#+:allegro
(excl::filesys-read-bytes (excl::stream-input-fn (ib-stream ib))
(ib-buf ib) 0 *bufsiz*)
#+:cmu
(system:read-n-bytes (ib-stream ib) (ib-buf ib) 0 *bufsiz*)
#+:lucid ; SYS_read => 3 in sunos4.1
(sys:syscall 3 (lcl:extract-stream-handle (ib-stream ib) :input)
(ib-buf ib) *bufsiz*)
#+:symbolics
(multiple-value-bind (ibuf first last)
; don't block when input isn't available...
(scl:send (ib-stream ib) :read-input-buffer nil nil)
(when (and first last (> last first))
(let ((n (min (- last first) *bufsiz*)))
(replace (ib-buf ib) ibuf :start1 0 :end1 n :start2 first :end2 last)
(scl:send (ib-stream ib) :advance-input-buffer (+ first n))
n))))
(setf (ib-offset ib) 0)
(values))
;;; read n chars from file
(defun test-ib (file n)
(let ((ib (open-ib file :direction :input)))
(unwind-protect
(dotimes (i n)
(%ib-read-byte ib))
(close-ib ib))))
Date: Tue, 23 Feb 1993 16:16 EST
From: Ted Dunning <···@nmsu.edu>
In article <····················@SUMMER.SCRC.Symbolics.COM> ···@stony-brook.scrc.symbolics.com (Scott McKay) writes:
The only thing that is silly is that there are no portable, buffered I/O
operations in Common Lisp, and the point of my message is that we should
address this deficiency in a principled, portable way.
hopefully by buffered i/o operations, i hope you mean the equivalent
of getchar. if not, then there still isn't any hope.
Yes.
and btw... i am curious how fast the C equivalent runs on the
symbolics.
that is, if it indeed does run on the symbolics.
There is a C compiler for the Symbolics, and I don't have it loaded.
Too much trouble. So, I dunno how fast it would run. But C is pretty
slow on a Symbolics Lisp Machine -- I can promise with 100% certainty
that it is not as fast as Lisp.
In article <····················@bingen.hrp.no> ······@bingen.hrp.no (Eyvind Ness) writes:
In article <·····@dime.cs.umass.edu> ·······@canberra.cs.umass.edu (Bob Krovetz) writes:
A while back there were some comments about Lisp being inefficient
in terms of I/O. Has anyone done any benchmarks to test this?
Perhaps reading and writing a million characters via read-char/
write-char vs. read-line/write-line, and comparing that to similar
functions in C and Fortran.
Often it is a bit inappropriate to use the low-level IO functions
WRITE-CHAR and READ-CHAR when you might be able to employ the
higher-level READ and PRINT functions.
sometimes this is true, sometimes it isn't.
After you have configured your own readtable with COPY-READTABLE,
SET-SYNTAX-FROM-CHAR, and SET-MACRO-CHARACTER, you can write simple and
efficient parsers in CL.
readtable hacking is an art that is rather more difficult than most of
the simple jobs we are talking about.
furthermore, readtables don't help a whit with binary files.
Another option to avoid using the portable (but often inefficient)
READ-CHAR and WRITE-CHAR, is to use implementation specific versions
like e.g. Lucid's FAST-READ/WRITE-CHAR/BYTE/STRING.
this isn't really an option any more than groveling around in the
internal structure of _iob is an option in C. it can be done, but if
it really needs to be done, then something is structurally wrong.
On 23 Feb 93 09:26:55, ······@bingen.hrp.no (Eyvind Ness) said:
>The most annoying limitation of this scheme is that some characters like
>":" belongs to a fixed character class. ":" is interpreted as a package
>marker, no matter what readtable you are using. I know that you can get
>around this in some CL implementations (e.g. Symbolics CL), but there is
>no portable way of doing it as far as I know, anyway.
I just wanted to say that you can do this in Allegro CL on Suns, because
I am doing it right now. We're reading in a large file which consists of
keyword/value pairs of the form:
<keyword>: <value>
The way that I do it is:
(set-syntax-from-char #\: #\;)
This way I just treat the : as a comment, and grab what I need. Simple,
but perhaps not elegant. But, it does work. It seems that, based on
CLtL2, that set-syntax-from-char was designed to do just this.
============================================================
Drew J. Asson ·····@stsci.edu
Advance Planning Systems Group (410) 338-4474
Space Telescope Science Institute
3700 San Martin Drive
Baltimore, MD 21218
--
============================================================
Drew J. Asson ·····@stsci.edu
Advance Planning Systems Group (410) 338-4474
Space Telescope Science Institute
3700 San Martin Drive
Baltimore, MD 21218