Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

macro question

13 views
Skip to first unread message

Rolf Wester

unread,
Mar 8, 2002, 9:59:11 AM3/8/02
to
Hi,

I have a function that depends on 2 variables (the real function has 3
arguments and is somewhat
more complicated):

(defun f (a b)
(* (sqrt a) b))

Now I have do call this function many times with different values for b
but constant a.
My idea was to use this macro:

(defmacro make-f (a)
(let ((c (sqrt a)))
`(defun f (b)
(* ,c b))))

With:
(defparameter a 1.0)
and
(make-f f a)
I get the error message (CLISP):

*** - argument to SQRT should be a number: A

With:
(make-f f #,a)

it works. Is there any way to get the macro work using (make-f a)
rather
than (make-f #,a)?

Thanks in advance.

Rolf Wester


Nils Goesche

unread,
Mar 8, 2002, 10:03:06 AM3/8/02
to
In article <3C88D1BF...@ilt.fhg.de>, Rolf Wester wrote:
> Hi,
>
> I have a function that depends on 2 variables (the real function has 3
> arguments and is somewhat
> more complicated):
>
> (defun f (a b)
> (* (sqrt a) b))
>
> Now I have do call this function many times with different values for b
> but constant a.
> My idea was to use this macro:

Why use a macro at all? How about

(defun make-f (a)
(let ((root (sqrt a)))
(lambda (b)
(* root b))))

Regards,
--
Nils Goesche
"The sooner all the animals are dead, the sooner we'll find
their money." -- Ed Bluestone
PGP key ID 0x42B32FC9

Rolf Wester

unread,
Mar 8, 2002, 10:15:04 AM3/8/02
to

Nils Goesche schrieb:

> In article <3C88D1BF...@ilt.fhg.de>, Rolf Wester wrote:
> > Hi,
> >
> > I have a function that depends on 2 variables (the real function has 3
> > arguments and is somewhat
> > more complicated):
> >
> > (defun f (a b)
> > (* (sqrt a) b))
> >
> > Now I have do call this function many times with different values for b
> > but constant a.
> > My idea was to use this macro:
>
> Why use a macro at all? How about
>
> (defun make-f (a)
> (let ((root (sqrt a)))
> (lambda (b)
> (* root b))))

This works of course but then I always have to funcall the function returned
by make-f
(seems I'm a little lazy). Besides this I'm interested in the macro solution
just for learning
more about how to write macros.

Thanks.

Rolf Wester


Nils Goesche

unread,
Mar 8, 2002, 10:34:52 AM3/8/02
to

Well, I would never use this, but you might find it interesting:

(defmacro with-f-and-a ((f a) &body body)
(let ((aval (gensym))
(b (gensym)))
`(let ((,aval ,a))
(flet ((,f (,b)
(funcall #',f ,aval ,b)))
,@body))))

and then

CL-USER 19 > (with-f-and-a (f 4)
(mapcar #'f '(1 2 3 4 5)))
(2.0 4.0 6.0 8.0 10.0)

CL-USER 20 > (with-f-and-a (f 4)
(list (f 1) (f 3)))
(2.0 6.0)

CL-USER 21 > (f 4 5)
10.0

CL-USER 22 >

Undoubtedly, there are a million ways this could fail to work :-)

Rolf Wester

unread,
Mar 8, 2002, 11:10:45 AM3/8/02
to

Nils Goesche schrieb:

I'm not really sure what this macro is intended to do. But it doesn't work.
With CLISP I get:

*** - EVAL/APPLY: too many arguments given to F


Regards.

Rolf Wester

Nils Goesche

unread,
Mar 8, 2002, 11:19:38 AM3/8/02
to
In article <3C88E285...@ilt.fhg.de>, Rolf Wester wrote:
>
>
> Nils Goesche schrieb:
>
>> Well, I would never use this, but you might find it interesting:
>>
>> (defmacro with-f-and-a ((f a) &body body)
>> (let ((aval (gensym))
>> (b (gensym)))
>> `(let ((,aval ,a))
>> (flet ((,f (,b)
>> (funcall #',f ,aval ,b)))
>> ,@body))))
>>
>> and then
>>
>> CL-USER 19 > (with-f-and-a (f 4)
>> (mapcar #'f '(1 2 3 4 5)))
>> (2.0 4.0 6.0 8.0 10.0)
>>
>> CL-USER 20 > (with-f-and-a (f 4)
>> (list (f 1) (f 3)))
>> (2.0 6.0)
>>
>> CL-USER 21 > (f 4 5)
>> 10.0
>>
>> CL-USER 22 >
>>
>> Undoubtedly, there are a million ways this could fail to work :-)
>
> I'm not really sure what this macro is intended to do. But it doesn't work.
> With CLISP I get:
>
> *** - EVAL/APPLY: too many arguments given to F

Whatever, it wasn't meant to be used, so there isn't much point
in discussing whether it is valid or not. I am still not sure
what your problem really is. What are you trying to do? Are
you looking for currying?

Rolf Wester

unread,
Mar 8, 2002, 11:47:26 AM3/8/02
to

Nils Goesche schrieb:

My point is performance. My function is:

(defun maxwell-v (v temp m)
(let ((c1 (* 4.0d0 dpi (expt (/ m (* 2.0d0 dpi +kB+ temp)) 1.5d0)))
(c2 (/ (* -1.0d0 m) (* 2.0d0 +kB+ temp))))
(* c1 v v(exp (* v v c2)))))

I don't want to calculate c1 and c2 every time I call the function with only v
different
than in the other calls. Currying would not help. A closure as you supposed:

(defun make-maxwell-v (temp m)
(let ((c1 (* 4.0d0 dpi (expt (/ m (* 2.0d0 dpi +kB+ temp)) 1.5d0)))
(c2 (/ (* -1.0d0 m) (* 2.0d0 +kB+ temp))))
(lambda (v)
(* c1 v v(exp (* v v c2))))))

does what I intend. I just was wondering wether this could also be done with a
macro?

Thanks for your time.

Regards

Rolf Wester

Kent M Pitman

unread,
Mar 8, 2002, 12:16:21 PM3/8/02
to
Rolf Wester <wes...@ilt.fhg.de> writes:

> Nils Goesche schrieb:
>
> > In article <3C88D1BF...@ilt.fhg.de>, Rolf Wester wrote:
> > > Hi,
> > >
> > > I have a function that depends on 2 variables (the real function has 3
> > > arguments and is somewhat
> > > more complicated):
> > >
> > > (defun f (a b)
> > > (* (sqrt a) b))
> > >
> > > Now I have do call this function many times with different values for b
> > > but constant a.
> > > My idea was to use this macro:
> >
> > Why use a macro at all? How about
> >
> > (defun make-f (a)
> > (let ((root (sqrt a)))
> > (lambda (b)
> > (* root b))))
>
> This works of course but then I always have to funcall the function
> returned by make-f (seems I'm a little lazy).

Strictly you want:

- - - -
(defmacro make-f (a)
`(let ((sqrt-of-a (sqrt-a ,a)))
(defun f (b)
(* sqrt-of-a b))))

(make-f 3)
- - - -

But as a point of style I'd instead recommend the following, which is
more general:

- - - -
(defmacro define-sqrt-multiplier (name a)
`(let ((sqrt-of-a (sqrt-a ,a)))
(defun ,name (b)
(* sqrt-of-a b))))

(define-sqrt-multiplier f 3)
- - - -

or:

- - - -
(defmacro define-multiplier (name quantity)
`(let ((quantity ,quantity))
(defun ,name (b)
(* quantity b))))

(define-multiplier f (sqrt 3))
- - - -

> Besides this I'm interested in the macro solution just for learning
> more about how to write macros.

This is not a good rationale.

"I want to use my screwdriver to hammer this nail. How do I do that?"
"Why not use a hammer?" "Well, I'm trying to learn how to use a
screwdriver."

Hmm.

Erik Naggum

unread,
Mar 8, 2002, 1:32:48 PM3/8/02
to
* Rolf Wester <wes...@ilt.fhg.de>
| My point is performance.

Are you quite sure about that?

| My function is:
|
| (defun maxwell-v (v temp m)
| (let ((c1 (* 4.0d0 dpi (expt (/ m (* 2.0d0 dpi +kB+ temp)) 1.5d0)))
| (c2 (/ (* -1.0d0 m) (* 2.0d0 +kB+ temp))))
| (* c1 v v(exp (* v v c2)))))
|
| I don't want to calculate c1 and c2 every time I call the function with
| only v different than in the other calls.

Common Lisp programmers generally do this with memoization. Several
general wrappers exist, but if you make a lot of calls in a row with only
v varying, as you say, here's a simple solution that computes c1 and c2
only when temp and m is different from those in the previous call. It
would also be natural to declare some types if you want more performance:

(defun maxwell-v (v temp m)

(declare (optimize (speed 3))
(double-float v temp m))
(let ((memo (load-time-value (vector nil nil nil nil))))
(declare (simple-vector memo))
(unless (and (eq (svref memo 0) temp)
(eq (svref memo 1) m))
(setf (svref memo 0) temp)
(setf (svref memo 1) m)
(setf (svref memo 2) (* 4.0d0 dpi (expt (/ m (* 2.0d0 dpi +kB+ temp)) 1.5d0)))
(setf (svref memo 3) (/ (* -1.0d0 m) (* 2.0d0 +kB+ temp))))
(* v v (the double-float (svref memo 2))
(exp (* v v (the double-float (svref memo 3)))))))

This _should_ be faster than calling a closure.

///
--
In a fight against something, the fight has value, victory has none.
In a fight for something, the fight is a loss, victory merely relief.

Rolf Wester

unread,
Mar 9, 2002, 4:05:28 AM3/9/02
to

Kent M Pitman wrote:

But as long as I'm in the position not to know exactly what a srewdriver is god
for I should
try it out and then find out that the hammer indead is better for the intended
purpose. I think
this is better than taking the first solution that works even if this solution
later proofs to be
the best one. How else can I become better in CL programming than trying things
out?

What do you recommend to be the hammer for my problem?
(Actually I'm not really to lazy to use funcall :) ).

Thank you for your helpful reply.

Rolf Wester

Rolf Wester

unread,
Mar 9, 2002, 5:28:40 AM3/9/02
to

Erik Naggum wrote:

> * Rolf Wester <wes...@ilt.fhg.de>
> | My point is performance.
>
> Are you quite sure about that?
>

I know that in the CL community performance isn't considered to be a serious design
goal. I would be happy if
perfomance wouldn't be so important in many of my projects. I'm doing numerical
simulations of physical
problems. Currently I'm working on a Monte Carlo code for simulating electrical
discharges. The program is
written in C++ and a run takes about a day. I would really prefer to do it in CL but
even the fastest CL implementations I tested where about five times slower in floating
point operations compared to C++. This would mean not 1 day but 5 (roughly estimated).
And presently I'm using only a simplified discharge model. In the general case it would
take to long to be practical even using C++. Of course using Fortran (which I seriously
considered)
or Assembler (which is beyond my skills) would make my code faster, using C++ is a
trade off between performance and expressive power.

In the actual problem performance isn't really a big problem. But the function will be
called thousands of times.
The time difference between calling the original function with three parameters and the
closure that only has one parameter is a factor of 3. Maybe this isn't worth the effort.

>
> | My function is:
> |
> | (defun maxwell-v (v temp m)
> | (let ((c1 (* 4.0d0 dpi (expt (/ m (* 2.0d0 dpi +kB+ temp)) 1.5d0)))
> | (c2 (/ (* -1.0d0 m) (* 2.0d0 +kB+ temp))))
> | (* c1 v v(exp (* v v c2)))))
> |
> | I don't want to calculate c1 and c2 every time I call the function with
> | only v different than in the other calls.
>
> Common Lisp programmers generally do this with memoization.

Is this pratical for functions with a very large number (1e6 -1e7) of parameter
combinations?

> Several
> general wrappers exist, but if you make a lot of calls in a row with only
> v varying, as you say, here's a simple solution that computes c1 and c2
> only when temp and m is different from those in the previous call. It
> would also be natural to declare some types if you want more performance:
>
> (defun maxwell-v (v temp m)
> (declare (optimize (speed 3))
> (double-float v temp m))
> (let ((memo (load-time-value (vector nil nil nil nil))))
> (declare (simple-vector memo))
> (unless (and (eq (svref memo 0) temp)
> (eq (svref memo 1) m))
> (setf (svref memo 0) temp)
> (setf (svref memo 1) m)
> (setf (svref memo 2) (* 4.0d0 dpi (expt (/ m (* 2.0d0 dpi +kB+ temp)) 1.5d0)))
> (setf (svref memo 3) (/ (* -1.0d0 m) (* 2.0d0 +kB+ temp))))
> (* v v (the double-float (svref memo 2))
> (exp (* v v (the double-float (svref memo 3)))))))
>
> This _should_ be faster than calling a closure.
>

Using CLISP it is about as fast as calling a closure either created by a function or a
macro.
I will also try it with CMUCL.

Thanks for your helpful reply (load-time-value was new to me, and I guess there are
quite a number of other
CL functions/forms that I'm still not aware of).

Regards

Rolf Wester

Kent M Pitman

unread,
Mar 9, 2002, 7:38:19 AM3/9/02
to
Rolf Wester <rolf....@t-online.de> writes:

[A bunch of technical solutions offered to your stated problem
about which no comment, not even a thank you, was made. Then...]

> > > Besides this I'm interested in the macro solution just for learning
> > > more about how to write macros.
> >
> > This is not a good rationale.
> >
> > "I want to use my screwdriver to hammer this nail. How do I do that?"
> > "Why not use a hammer?" "Well, I'm trying to learn how to use a
> > screwdriver."
>
> But as long as I'm in the position not to know exactly what a
> srewdriver is god for I should try it out and then find out that the
> hammer indead is better for the intended purpose.

When you're told you're using the wrong tool, you should listen.

Or else you should identify your problem differently.

There are better ways to solve the problem you expressed.
They do not involve macros.

There are better ways to learn about macros.
They do not involve the problem you expressed.

It's fine when you ask your question to ask it any way you want, but when
skilled experts tell you that you have drifted afield of the right way to
use something, it's worth listening.

Speaking generally now, and not just about this one issue: We who
answer questions here are all volunteers. We don't owe time here. We
risk time here. We risk it on the hope that it will be of benefit.
When someone starts to show that they're determined to do or think
whatever they want to do, regardless of what we say, it makes us feel
like time ill spent or like the person we're talking to is not generally
a good risk for answers to future questions. I'd be surprised if you
wanted either of these effects.

> I think this is
> better than taking the first solution that works even if this
> solution later proofs to be the best one. How else can I become
> better in CL programming than trying things out?

The issue isn't whether you take or don't take a solution that you yourself
generate. The issue is whether you take or don't take advice once offered.

The advice "don't use macros here" was excellent advice.

A response like "Oh, I thought this was going to be a good way to learn
about macros. Can you suggest a better way to learn about macros." would
have been a great way to proceed.

A response like "But I really want to use macros here." is a lot less good.

> What do you recommend to be the hammer for my problem?

Just as was expressed in the other case. Use functions.
It was good advice.

(defun make-f (a)
(let ((sqrta (sqrt a)))
(lambda (b) (* a b))))

You complained about use of FUNCALL, which was reasonable. But then you
said you were determined to use macros, suggesting the problem was just
made up and that you didn't care about it. This in turn caused people
not to offer you further suggestions, which you are now complaining about.
Yet, effectively, that's what you asked for. If it's the solution to the
problem you cared about, then identify THAT as your priority. Then someone
might say:

(defun make-f (a)
(let ((sqrta (sqrt a)))
(setf (symbol-function 'f) (lambda (b) (* a b)))))

(make-f 3)

Though again I would prefer

(defun declare-multiplier (symbol amount)
(setf (symbol-function symbol) (lambda (b) (* amount b))))

(declare-multiplier 'f (sqrt 3))

You will note I keep changing the function name. Defining macros should
be called something like DEFINE-xxx so that you can tell on sight that they
do not evaluate their args, or that they evaluate them strangely. I
personally reserve the term MAKE-xxx for something that makes and returns
a value. If it's purpose is primariliy to work by side-effect, I prefer
DECLARE-xxx. Often for DEFINE-xxx macros, I make an underlying DECLARE-xxx
function to be the worker function for it. Some might not agree with my use
of DECLARE because it perhaps fights the DECLARE symbol used in binding forms,
though, which is not evaluated. In any case, it's less important what
morpheme/prefix you use, and more important that you just pick a convention
and use it consistently so that others reading your code can predict and
interpret subtle meanings like this among functions in your chosen style.

> (Actually I'm not really to lazy to use funcall :) ).

This kind of remark after someone's had a long discussion predicated on
the idea that they have previously not satisfied you probably won't
win you a lot of points either.

Kent M Pitman

unread,
Mar 9, 2002, 8:18:50 AM3/9/02
to
Rolf Wester <rolf....@t-online.de> writes:

> Erik Naggum wrote:
>
> > * Rolf Wester <wes...@ilt.fhg.de>
> > | My point is performance.
> >
> > Are you quite sure about that?
>
> I know that in the CL community performance isn't considered to be
> a serious design goal.

There are two parts to the verb "to know".

1. to believe
2. to be true

Your remark fails on the second of these.

You may well believe that performance is not a serious design goal, but it is.

I don't know where you got the idea it wasn't, but it could be from the
personal opinions of some posters here, myself included, who are not fans
of performance issues. That doesn't mean that the language isn't designed
with performance in mind or that we don't endorse the prioritization of
performance in the language design. I've certainly happily voted in favor
of decisions that have accomodated performance. Performance capabilities
should be a major priority in language design.

HOWEVER, that doesn't mean that I think that users should care as much
as they do about performance. A lot of people want to build
performance in from the start. That's often misguided. Certainly
building in algorithmic performance is important, but few reasonable
languages get in the way of that, and so being algorithmically
efficient is rarely about exploiting language features, other than in
the trivial sense that you can't write something down in a language
without exploiting the language to at least some degree. But what
most people mean by performance is making sure that they get every
last cycle out of certain critical parts. And mostly you can't do
that until (a) the code is in final form, (b) the platform is known,
and (c) a good understanding of the patterns of runtime data usage are
available. Then you can meter performance and fix bottlenecks.

I would, without proof, venture that 95% of code (not just Lisp code
but any code) never reaches production deployment, and I'm probably
being generous. It's probably more like 99.nn% where nn still has
lots of 9's in it. People write code and they throw it away. They
write more code and they throw it away. They toy around with things.
They build things they hope will be products. etc. You can optimize
this stuff to death but it will only keep you from getting to the next
project that maybe is really the one that will get out the door.
There is a reasonable ordering to the optimization of code and it is
"develop first, optimize second".

Once in a GREAT while, there is a program with data so strange that it
can't even be tested for development without extreme optimization, but
that is certainly not the rule, it's the exception.

Note when I say "optimize second", I mean second in the order of a set
of linearized steps to peform. This is not the same as "being secondary",
as in "this is less of a priority".

We had a huge confusion about this with non-English speakers one day
in a meeting of the ISO group working on ISLISP. We were talking
about our "primary goal" and someone who spoke French overheard some
discussions among the French delegation who had translated this to
"first goal" because "primary" sounded like "premiere" (or some such
word--whatever means "first" in French). The result was that some
people thought they were voting on "first goal" and some were voting
on our "most important goal". These are not the same.

I think performance is a primary priority in language design, but it's
not a "first step" in programming.

Incidentally, one reason for this that comes up in your example is the
performance characteristics of your target machine. Optimizing for CLISP
is _very_ different than optimizing for nearly any other Lisp. The fact
that is is byte compiled makes what are often slow operations in other
implementations be (at least relatively) much faster in CLISP, and
vice versa. If you perturb the basic structure of your program around
a belief that you either are or are not using CLISP, you will be programming
yourself into a corner that veritably requires you to use CLISP for delivery.

> > | My function is:
> > |
> > | (defun maxwell-v (v temp m)
> > | (let ((c1 (* 4.0d0 dpi (expt (/ m (* 2.0d0 dpi +kB+ temp)) 1.5d0)))
> > | (c2 (/ (* -1.0d0 m) (* 2.0d0 +kB+ temp))))
> > | (* c1 v v(exp (* v v c2)))))
> > |
> > | I don't want to calculate c1 and c2 every time I call the function with
> > | only v different than in the other calls.
> >
> > Common Lisp programmers generally do this with memoization.
>
> Is this pratical for functions with a very large number (1e6 -1e7)
> of parameter combinations?

At this point, we'll assume your program is debugged and that you have run
it and it's unacceptably slow, and that you've identified this as the thing
that needs speeding up. At this point, a discussion of optimization is
definitely in order.

I don't like the idea of memoizing not becuase of the large number of
combinations but because of the unpredictable constant factor time of
producing a hash key, which might not be much less than the cost of
computing c1 and c2.

I don't see anything wrong with you currying the arguments to the function
by taking an invariant.

Depending on the situation, you may not want to even cons a closure.
Another way to do this is:

(defun maxwell-v (v temp m

&optional (c1 (* 4.0d0 dpi

(expt (/ m (* 2.0d0 dpi +kB+ temp))
1.5d0)))
(c2 (/ (* -1.0d0 m) (* 2.0d0 +kB+ temp))))

(values (* c1 v v (exp (* v v c2))) c1 c2))

So that you could do:

(let ((c1 nil) (c2 nil))
(dolist (v v-list)
(multiple-value-setq (result c1 c2)
(maxwell-v v temp m c1 c2))))

But this ends up doing the setup and return of c1, c2 even when not needed.
You're also passing temp and m when not needed. This is extra work you don't
need on what you maybe know to be a tight inner loop that needs every cycle
it can get. I might prefer

(defun maxwell-v-c1-c2 (temp m)
(values (* 4.0d0 dpi (expt (/ m (* 2.0d0 dpi +kB+ temp)) 1.5d0))


(/ (* -1.0d0 m) (* 2.0d0 +kB+ temp))))

(defun maxwell-v-fast (v c1 c2)
(* c1 v v (exp (* v v c2))))

... (multiple-value-bind (c1 c2)
(maxwell-v-c1-c2 temp m)
(dolist (v v-list)
... (maxwell-v-fast v c1 c2) ...)) ...

Of course, if you are doing monte-carlo stuff, maybe you don't get a
nice vectorized set of v's that are all together under the same temp
and m. This is what I meant about patterns of runtime data. I
noticed upthread you were discussing caching the most recent value of
c1 & c2 just in case it's able to be reused, but I don't know how
often that comes up either. Maintaining such a cache, especially if it's
a one-element cache, may be more cost than it's worth if every time you
randomize you're in a different place along all three axes v, temp, and m.

This is all I'm going to say on optimization. Normally I would just leave
it to a consultant to do, frankly. It is not fun to help people optimize
stuff on a newsgroup. It is tricky to do right and largely thankless.
However, I wanted to show at least a little analysis on this to show that
various optimization strategies are possible and that your original claim,
that performance is not a design goal of CL, was just false.

Rolf Wester

unread,
Mar 9, 2002, 12:33:24 PM3/9/02
to Kent M Pitman

Kent M Pitman wrote:

> Rolf Wester <rolf....@t-online.de> writes:
>
> [A bunch of technical solutions offered to your stated problem
> about which no comment, not even a thank you, was made. Then...]
>
> > > > Besides this I'm interested in the macro solution just for learning
> > > > more about how to write macros.
> > >
> > > This is not a good rationale.
> > >
> > > "I want to use my screwdriver to hammer this nail. How do I do that?"
> > > "Why not use a hammer?" "Well, I'm trying to learn how to use a
> > > screwdriver."
> >
> > But as long as I'm in the position not to know exactly what a
> > srewdriver is god for I should try it out and then find out that the
> > hammer indead is better for the intended purpose.
>
> When you're told you're using the wrong tool, you should listen.

>
> Or else you should identify your problem differently.
>
> There are better ways to solve the problem you expressed.
> They do not involve macros.
>
> There are better ways to learn about macros.
> They do not involve the problem you expressed.
>
> It's fine when you ask your question to ask it any way you want, but when
> skilled experts tell you that you have drifted afield of the right way to
> use something, it's worth listening.

Of course I do listen to this and I greatly appreciate that there are people who
take the time
to answer my questions. I didn't want to express that I don't trust what experts
tell
me. If macros aren't the right tool for my problem I appreciate when someone tells
me that
it isn't. But besides accepting the fact I also want to understand why macros
aren't the best tool.
I guess that I have the problem of not being able to clearly state what I really
want to know. Maybe
this is at least partly due to English not being my native language.

>
> Speaking generally now, and not just about this one issue: We who
> answer questions here are all volunteers. We don't owe time here. We
> risk time here. We risk it on the hope that it will be of benefit.
> When someone starts to show that they're determined to do or think
> whatever they want to do, regardless of what we say, it makes us feel
> like time ill spent or like the person we're talking to is not generally
> a good risk for answers to future questions. I'd be surprised if you
> wanted either of these effects.
>

Of course not. I really appreciate that you and others spend so much time to
answer
my questions. Maybe it's my kind of questioning that sometimes looks like I'm
arguing
against experts statements but my objective is to just get more information to
make
the point more clear for me. I'm a Lisp beginner and I know that you, Eric Naggum,
Nils
Goesche and others are much more skilled in that field than I will ever be.

>
> > I think this is
> > better than taking the first solution that works even if this
> > solution later proofs to be the best one. How else can I become
> > better in CL programming than trying things out?
>
> The issue isn't whether you take or don't take a solution that you yourself
> generate. The issue is whether you take or don't take advice once offered.
>
> The advice "don't use macros here" was excellent advice.
>
> A response like "Oh, I thought this was going to be a good way to learn
> about macros. Can you suggest a better way to learn about macros." would
> have been a great way to proceed.
>
> A response like "But I really want to use macros here." is a lot less good.
>

You are right. My point was not say: "What ever you tell, I want to use macros."
I didn't express myself correctly. I accepted the statement that a macro isn't the
right
tool. The point was I wanted to know how I can do calculations at macro expansion
time not only for the problem at hand:
(defmacro mm (name a)
(let ((b (* 2 a)))
`(defun ,name (c)
(* b c))))
so that:
(mm f 2)
expands to:
(defun f (c)
(* 4 c))

I should have made this explicit.

>
> > What do you recommend to be the hammer for my problem?
>
> Just as was expressed in the other case. Use functions.
> It was good advice.
>
> (defun make-f (a)
> (let ((sqrta (sqrt a)))
> (lambda (b) (* a b))))
>
> You complained about use of FUNCALL, which was reasonable. But then you
> said you were determined to use macros, suggesting the problem was just
> made up and that you didn't care about it. This in turn caused people
> not to offer you further suggestions, which you are now complaining about.
>

My fault. I will try to make my points clearer in the future.

> Yet, effectively, that's what you asked for. If it's the solution to the
> problem you cared about, then identify THAT as your priority. Then someone
> might say:
>
> (defun make-f (a)
> (let ((sqrta (sqrt a)))
> (setf (symbol-function 'f) (lambda (b) (* a b)))))
>
> (make-f 3)
>
> Though again I would prefer
>
> (defun declare-multiplier (symbol amount)
> (setf (symbol-function symbol) (lambda (b) (* amount b))))
>
> (declare-multiplier 'f (sqrt 3))
>
> You will note I keep changing the function name. Defining macros should
> be called something like DEFINE-xxx so that you can tell on sight that they
> do not evaluate their args, or that they evaluate them strangely. I
> personally reserve the term MAKE-xxx for something that makes and returns
> a value. If it's purpose is primariliy to work by side-effect, I prefer
> DECLARE-xxx. Often for DEFINE-xxx macros, I make an underlying DECLARE-xxx
> function to be the worker function for it. Some might not agree with my use
> of DECLARE because it perhaps fights the DECLARE symbol used in binding forms,
> though, which is not evaluated. In any case, it's less important what
> morpheme/prefix you use, and more important that you just pick a convention
> and use it consistently so that others reading your code can predict and
> interpret subtle meanings like this among functions in your chosen style.
>

Thank you for your extensive explanations.

>
> > (Actually I'm not really to lazy to use funcall :) ).
>
> This kind of remark after someone's had a long discussion predicated on
> the idea that they have previously not satisfied you probably won't
> win you a lot of points either.

I accept your critique. As I already pointed out I probably miss to express myself

clearly enough. What I wanted to say was that when you (or Nils) tell me that
using
a closure that has to be funcall'ed is the best solution to my problem I will not
reject
that solution just because I'm to lazy to use funcall.

Hoping that I could somewhat clarify my intentions and attitudes I want to thank
you
and also Nils and Eric for taking the time to answer my questions. I really
appreciate
your efforts and I do listen to what you say.

Regards

Rolf Wester


Rolf Wester

unread,
Mar 9, 2002, 12:50:12 PM3/9/02
to

Kent M Pitman wrote:

Thanks a lot for your detailed reply. It's very interesting for me and I have
learned
quite a lot.

Regards

Rolf Wester

Felix Schlesinger

unread,
Mar 9, 2002, 8:34:46 AM3/9/02
to
Kent M Pitman schrieb

> There are two parts to the verb "to know".
>
> 1. to believe
> 2. to be true

Actualy, there are 3. You forgot
3. to be justified in believing

;-)
Ciao
Felix

Erik Naggum

unread,
Mar 9, 2002, 1:14:57 PM3/9/02
to
* Rolf Wester <wes...@ilt.fhg.de>
| My point is performance.

* Erik Naggum wrote:
> Are you quite sure about that?

* Rolf Wester <rolf....@t-online.de>


| I know that in the CL community performance isn't considered to be a
| serious design goal.

Then you "know" wrong. You also read what I wrote completley wrong, for
which you may easily be forgiven: Your point seemed to be do it Your Way.

Performance is vitally important and a veyy serious design goal of the
serious developers , but it so happens that it is not at the top of the
list: Correctness is more important than performaing incorrectly faster.
C++ has a slightly different priorities. _Java_ is fairly accused of not
having performance as a serious design goal, but some people still have.

| I would be happy if perfomance wouldn't be so important in many of my
| projects.

I am puzzled by your use of CLISP if you want performance. It has a very
peculiar performance profile, and no tools to help you discover where to
optimize your code. Use a commercial Common Lisp implementation like
Allegro CL or LispWorks if you want the kind of performance that others
are willing to pay for.

| The program is written in C++ and a run takes about a day.

Yikes.

| I would really prefer to do it in CL but even the fastest CL
| implementations I tested where about five times slower in floating point
| operations compared to C++.

Which CL implementation was that? Did you declare the types of all
relevant variables to be doublt-float? (It does not matter in CLISP, but
both CMUCL and Allegro CL benefit greatly from such declarations.)

| This would mean not 1 day but 5 (roughly estimated).

Well, gee, I have almost 2GHz of spare CPU power at any time, and you
have none? :) Seriously, if it is possible to divide up your problem
into separately calculable chunks, like the SETI project does, you could
use some of the amazing amount of unused cycles around the world, or just
your lab.

| And presently I'm using only a simplified discharge model. In the general
| case it would take to long to be practical even using C++. Of course
| using Fortran (which I seriously considered) or Assembler (which is
| beyond my skills) would make my code faster, using C++ is a trade off
| between performance and expressive power.

It might be possible to write much more complex (and faster) C++ with
Common or Emacs Lisp, or even to write C code so you could write code
that would be impossible to maintain manually. When I had to work with
C++ (fall of 1993), it was so painful for me that I wrote a lot of Emacs
Lisp code to produce C++ for me. This turned out to be a good basis for
my transition to Common Lisp after I had made up my mind never to waste
my time on anything so ill-designed as C++ again. (Things may have
changed. Nobody I trust think I would be any happier with C++ today. :)

| In the actual problem performance isn't really a big problem.

One of the solutions to performance is not to do so much work. You could
refactor your algorithms so you keep a large number of intermediate
results around.

| But the function will be called thousands of times. The time difference
| between calling the original function with three parameters and the
| closure that only has one parameter is a factor of 3. Maybe this isn't
| worth the effort.

It appears to me to be smarter to compute and pass c1 and c2 to such a
function than passing temp and m. You could also obtain unboxed floating
point calculations by not calling any user functions in your code. This
is sometimes extremely useful advice despite the high overhead of coding
in this style. But if a single run takes a day, you have time to tinker
with the code, too.

| Is this pratical for functions with a very large number (1e6 -1e7) of parameter
| combinations?

That is irrelevant. It might be useful to store the mapping from a set
of parameters to the result of a function call in an external database,
as long as it takes longer to compute than to fetch a pre-computed value,
even with billions of operations. E.g., a caching DNS server for a web
crawler has a tremendous amount of time available to optimize and keep
around old values, compared to the time and bandwidth cost of using the
DNS to find a non-expired value. (Some hobbyist DNS implementations, one
in Common Lisp, violate the expiration time to save you the trouble of
getting correct results when a site changes address. This is the wrong
kind of optimization.)

| Using CLISP it is about as fast as calling a closure either created by a
| function or a macro. I will also try it with CMUCL.

Please do. CLISP is not a performance-oriented implementations, although
its internal performance is good enough that it offsets the slow byte
code interpreter somewhat. (It would be interesting to make a JIT
compiler for CLISP.)

| Thanks for your helpful reply (load-time-value was new to me, and I guess
| there are quite a number of other CL functions/forms that I'm still not
| aware of).

My pleasure. One of the nice things about Common Lisp is that it is
nearly sufficient to learn the language from the specification -- you do
not need a large number of books about how to use it because of all the
idioms you have to master, as well.

Duane Rettig

unread,
Mar 9, 2002, 3:00:01 PM3/9/02
to

[Others have responded to the rest of this, but I want to make a
pointed comment about your opening statement]:

Rolf Wester <rolf....@t-online.de> writes:

> I know that in the CL community performance isn't considered to be a serious design
> goal.

Design goals must follow the requirements specifications to which
they are targeted. Thus, if the requirements include a performance
factor, then the design _must_ address this requirement. If the
requirements do not include performance, then any performance
consideration in the design is superfluous.

What you may actually be perceiving from the CL community is the
fact that we don't automatically assume an implied performance
criterion when we design. We always tend to say "get it right",
which really means "meet the requirements". If the requirements
includes a minimum performance requirement, then getting it right
_includes_ meeting that minimum performance.

Sometimes performance is specified as a requirement as "faster than
the other guy". Other times, it is "faster than before". Many times,
more than you might think, it is "fast enough so that I don't have to
feel like going to get a cup of coffee while I'm waiting..."

When talking about performance in the CL community, you must make your
requirement explicit, otherwise you will not be understood when it finally
comes out...

Duane Rettig Franz Inc. http://www.franz.com/ (www)
1995 University Ave Suite 275 Berkeley, CA 94704
Phone: (510) 548-3600; FAX: (510) 548-8253 du...@Franz.COM (internet)

Kent M Pitman

unread,
Mar 9, 2002, 5:37:51 PM3/9/02
to
fam_Sch...@t-online.de (Felix Schlesinger) writes:

I guess perhaps there's some debate about that. I've heard claims of
this broad class before, but I don't buy them.

I think being right justifies your belief. Otherwise, faith has no purpose
in the world.

I think, for example, that Fermat "knew", proof or not, if he in fact
believed (which he may not have--he may have thought he was having fun
at others' expense).

I think it is sufficient to merely believe something on faith, and to also
be right, in order to have been said to have known.

Otherwise, I am not sure one can ever ground out "to know" in any way.
I'm not sure what would be acceptable to justify 3.

Thomas F. Burdick

unread,
Mar 9, 2002, 6:27:16 PM3/9/02
to
Erik Naggum <er...@naggum.net> writes:

> You could also obtain unboxed floating point calculations by not
> calling any user functions in your code. This is sometimes
> extremely useful advice despite the high overhead of coding in
> this style. But if a single run takes a day, you have time to
> tinker with the code, too.

I think the best advice for the OP at this point is to pick a
performance-oriented implementation, and learn about it. Eg, the
above isn't necessary in CMUCL, if you do things right, taking
advantage of block compilation. But if the problem is so
performance-critical that you'd change your coding style so radically,
it's *really* important to pick a particular implementation to
optimize for.

--
/|_ .-----------------------.
,' .\ / | No to Imperialist war |
,--' _,' | Wage class war! |
/ / `-----------------------'
( -. |
| ) |
(`-. '--.)
`. )----'

Rolf Wester

unread,
Mar 10, 2002, 4:18:38 AM3/10/02
to

Erik Naggum wrote:

> * Rolf Wester <wes...@ilt.fhg.de>
> | My point is performance.
>
> * Erik Naggum wrote:
> > Are you quite sure about that?
>
> * Rolf Wester <rolf....@t-online.de>
> | I know that in the CL community performance isn't considered to be a
> | serious design goal.
>
> Then you "know" wrong. You also read what I wrote completley wrong, for
> which you may easily be forgiven: Your point seemed to be do it Your Way.
>
> Performance is vitally important and a veyy serious design goal of the
> serious developers , but it so happens that it is not at the top of the
> list: Correctness is more important than performaing incorrectly faster.
> C++ has a slightly different priorities. _Java_ is fairly accused of not
> having performance as a serious design goal, but some people still have.
>
> | I would be happy if perfomance wouldn't be so important in many of my
> | projects.
>
> I am puzzled by your use of CLISP if you want performance. It has a very
> peculiar performance profile, and no tools to help you discover where to
> optimize your code. Use a commercial Common Lisp implementation like
> Allegro CL or LispWorks if you want the kind of performance that others
> are willing to pay for.

The point is that at work I have a NT PC (CLISP) and a Compaq Tru64 workstation
(CMUCL)
which I access over our intranet. It's more comfortable to do development on the PC
and do
the long runs on the Compaq machine. I'm going to install Linux on the PC too (not so
easy to get
it through in the the institute I'm working at). The problem with the commercial
implementations is
that they are to expensive to get it easily payed by my employer especially if I
wanted to have it
both on the PC and the workstation (I'm working at a reserch institute and we get
Visual C++
at a much much lower price). First I would have to show that using CL would be a
great benefit
for the institute. I'm the only one amoung my colleagues even knowing CL. The trial
versions of
ACL or LispWorks are to limited to be useful for me.

>
> | The program is written in C++ and a run takes about a day.
>
> Yikes.
>
> | I would really prefer to do it in CL but even the fastest CL
> | implementations I tested where about five times slower in floating point
> | operations compared to C++.
>
> Which CL implementation was that? Did you declare the types of all
> relevant variables to be doublt-float? (It does not matter in CLISP, but
> both CMUCL and Allegro CL benefit greatly from such declarations.)
>

I tried ACL6 on the PC and CMUCL on the Compaq machine. I used
declarations (I asked in this newsgroup how to do it best). In both cases
C++ was about 5 times faster on a simplified 2-dimensional diffusion problem.
The biggest performance problem arose when reading big files (30 MB).
This was much slower than C++. I couldn't try it with ACL Trial because of the heap
limitations.

> | This would mean not 1 day but 5 (roughly estimated).
>
> Well, gee, I have almost 2GHz of spare CPU power at any time, and you
> have none? :) Seriously, if it is possible to divide up your problem
> into separately calculable chunks, like the SETI project does, you could
> use some of the amazing amount of unused cycles around the world, or just
> your lab.
>

I considered using MPI. Some of my colleagues are going to set up a Linux cluster
and using MPI for parallel computing. But I don't know of a CL MPI interface. The
nearby Aachen Technical Institute recently got a SunFire cluster that I could access.

But I guess if I would ask for a CL installation they would look at me as if I would
be an alien. They only support Fortran and C++. The main disadvantage would be that
I would have to share the computing power amoung many users so that at the end using
the SunFire wouldn't be faster than using the Compag machine which runs only for me.

>
> | And presently I'm using only a simplified discharge model. In the general
> | case it would take to long to be practical even using C++. Of course
> | using Fortran (which I seriously considered) or Assembler (which is
> | beyond my skills) would make my code faster, using C++ is a trade off
> | between performance and expressive power.
>
> It might be possible to write much more complex (and faster) C++ with
> Common or Emacs Lisp, or even to write C code so you could write code
> that would be impossible to maintain manually. When I had to work with
> C++ (fall of 1993), it was so painful for me that I wrote a lot of Emacs
> Lisp code to produce C++ for me. This turned out to be a good basis for
> my transition to Common Lisp after I had made up my mind never to waste
> my time on anything so ill-designed as C++ again. (Things may have
> changed. Nobody I trust think I would be any happier with C++ today. :)
>

For arrays I turned to TNT, a collection of template classes that allow more secure
array access. With bounds check turned on this is a great advantage over the simple
C arrays :). Of course this slows down the program :(. I'm going to benchmark TNT
with
bounds check turned on. Maybe that it turns out that CMUCL isn't so much behind this.

I did it. CMUCL is much faster than CLISP for this problem but there is still
virtually no timing difference between the solution suggested by you and the closure.

>
> | Thanks for your helpful reply (load-time-value was new to me, and I guess
> | there are quite a number of other CL functions/forms that I'm still not
> | aware of).
>
> My pleasure. One of the nice things about Common Lisp is that it is
> nearly sufficient to learn the language from the specification -- you do
> not need a large number of books about how to use it because of all the
> idioms you have to master, as well.
>

But I have to admit that tutorials and Graham's book "ANSI Common Lisp" was quite
helpful for me. And I guess without the competent help that one can get here learning

CL would be much more difficult (at least for me).

Johannes Grødem

unread,
Mar 10, 2002, 5:27:58 AM3/10/02
to
* Rolf Wester <rolf....@t-online.de>:

> But I have to admit that tutorials and Graham's book "ANSI Common
> Lisp" was quite helpful for me. And I guess without the competent
> help that one can get here learning CL would be much more difficult
> (at least for me).

Have you read the CMUCL User's Manual? It's got some useful
performance tips.

(Also, do you think you could set your newsreader to wrap at 80
columns?)

--
johs

Erik Naggum

unread,
Mar 10, 2002, 8:32:58 AM3/10/02
to
* Rolf Wester

| The point is that at work I have a NT PC (CLISP) and a Compaq Tru64
| workstation (CMUCL) which I access over our intranet. It's more
| comfortable to do development on the PC and do the long runs on the
| Compaq machine. I'm going to install Linux on the PC too (not so easy to
| get it through in the the institute I'm working at). The problem with
| the commercial implementations is that they are to expensive to get it
| easily payed by my employer especially if I wanted to have it both on the
| PC and the workstation (I'm working at a reserch institute and we get
| Visual C++ at a much much lower price).

If you are an accredited research institute, you get _serious_ discounts
from Franz Inc, at lest.

| First I would have to show that using CL would be a great benefit for the
| institute. I'm the only one amoung my colleagues even knowing CL. The
| trial versions of ACL or LispWorks are to limited to be useful for me.

And they are not even useful to show that you can develop faster than the
runtime supposedly suffers? Remember, if you spend a week tinkering with
your C++ solution, your Common Lisp solution would already have the
answer.

| For arrays I turned to TNT, a collection of template classes that allow
| more secure array access. With bounds check turned on this is a great
| advantage over the simple C arrays :). Of course this slows down the
| program :(. I'm going to benchmark TNT with bounds check turned on. Maybe
| that it turns out that CMUCL isn't so much behind this.

An interesting optimize declaration you may want to look at is (safety 0)
if you have not already tried it. I am not sure what CMUCL does with it,
but Allegro CL can get seriously faster with this declaration.

Duane Rettig has also posted and made available some information on how
to write code with unboxed floats. I have never actually tried this, but
from reports of people who have, it seems to be exceptionally efficient.

Now, the only problem I have with floating point on the IA32, is that the
hardware is much better than the languages that are usually used on it.
Floating-point on that hardware uses an internal 80-bit representation,
which is what you really want to compute with. Hand-writing assembly
code to handle some of these things is actually well worth the effort if
it takes days to do a run. IA32 floating point is easy to program and it
can be done reasonably elegantly. It even has a good number of registers
that are not tied up with nonsense. It may well be the only thing that
is done right on that architecture's instruction set side.

| I did it. CMUCL is much faster than CLISP for this problem but there is
| still virtually no timing difference between the solution suggested by
| you and the closure.

That is quite interesting to hear. However, have you tried to refactor
the algorithm and compute c1 and c2 outside the function and pass them
in? Have you tried writing a compiler-macro that does this for you?

| But I have to admit that tutorials and Graham's book "ANSI Common Lisp"
| was quite helpful for me. And I guess without the competent help that one
| can get here learning CL would be much more difficult (at least for me).

I once told a friend who was interested in learning Common Lisp that the
only way to learn the language well was to find a community of people who
used it. The problem is that such communities are somewhat hard to find.
You can much too easily exhaust an electronic forum.

Rolf Wester

unread,
Mar 10, 2002, 9:39:47 AM3/10/02
to

Johannes Grødem wrote:

> * Rolf Wester <rolf....@t-online.de>:
>
> > But I have to admit that tutorials and Graham's book "ANSI Common
> > Lisp" was quite helpful for me. And I guess without the competent
> > help that one can get here learning CL would be much more difficult
> > (at least for me).
>
> Have you read the CMUCL User's Manual? It's got some useful
> performance tips.

I'm just going through the compiler part.

>
>
> (Also, do you think you could set your newsreader to wrap at 80
> columns?)
>

Oh. I wasn't aware of that. In my newsreader wrapping is O.K.

Rolf

>
> --
> johs

Rolf Wester

unread,
Mar 10, 2002, 12:13:52 PM3/10/02
to

Erik Naggum wrote:

> If you are an accredited research institute, you get _serious_ discounts
> from Franz Inc, at lest.

I know. But the prize is still much higher than what we have to pay for VC++
(because it's almost nothing in the latter case).

>
> | First I would have to show that using CL would be a great benefit for the
> | institute. I'm the only one amoung my colleagues even knowing CL. The
> | trial versions of ACL or LispWorks are to limited to be useful for me.
>
> And they are not even useful to show that you can develop faster than the
> runtime supposedly suffers?

With a heap size limit of 15 MB not really. My PC has 520 MB RAM. This is
not oversized for many of my programs.

> Remember, if you spend a week tinkering with
> your C++ solution, your Common Lisp solution would already have the
> answer.
>

This would be true for you but not necessaryly for a lisp beginner like me.
At present this is a problem for me. I know C++ well enough to be able
to do my work in a reasonable or at least predictable time. At present I
don't have the same confidence in my Lisp skills.

> | For arrays I turned to TNT, a collection of template classes that allow
> | more secure array access. With bounds check turned on this is a great
> | advantage over the simple C arrays :). Of course this slows down the
> | program :(. I'm going to benchmark TNT with bounds check turned on. Maybe
> | that it turns out that CMUCL isn't so much behind this.
>
> An interesting optimize declaration you may want to look at is (safety 0)
> if you have not already tried it. I am not sure what CMUCL does with it,
> but Allegro CL can get seriously faster with this declaration.
>
> Duane Rettig has also posted and made available some information on how
> to write code with unboxed floats. I have never actually tried this, but
> from reports of people who have, it seems to be exceptionally efficient.
>
> Now, the only problem I have with floating point on the IA32, is that the
> hardware is much better than the languages that are usually used on it.
> Floating-point on that hardware uses an internal 80-bit representation,
> which is what you really want to compute with. Hand-writing assembly
> code to handle some of these things is actually well worth the effort if
> it takes days to do a run. IA32 floating point is easy to program and it
> can be done reasonably elegantly. It even has a good number of registers
> that are not tied up with nonsense. It may well be the only thing that
> is done right on that architecture's instruction set side.

I guess I should learn about IA32 assembler.

>
> | I did it. CMUCL is much faster than CLISP for this problem but there is
> | still virtually no timing difference between the solution suggested by
> | you and the closure.
>
> That is quite interesting to hear. However, have you tried to refactor
> the algorithm and compute c1 and c2 outside the function and pass them
> in?

No I haven't done that yet. But I will try it.

> Have you tried writing a compiler-macro that does this for you?
>

I read about compiler-maros before but have no clear understanding what this
is. Can you give me a link
to somewhere I can learn more about it?

>
> | But I have to admit that tutorials and Graham's book "ANSI Common Lisp"
> | was quite helpful for me. And I guess without the competent help that one
> | can get here learning CL would be much more difficult (at least for me).
>
> I once told a friend who was interested in learning Common Lisp that the
> only way to learn the language well was to find a community of people who
> used it. The problem is that such communities are somewhat hard to find.

It's indeed hard being a one man show (amoung my colleagues at least).

Regards

Rolf Wester


Bulent Murtezaoglu

unread,
Mar 10, 2002, 2:36:54 PM3/10/02
to
>>>>> "RW" == Rolf Wester <rolf....@t-online.de> writes:
[...]
RW> The point is that at work I have a NT PC (CLISP) and a Compaq
RW> Tru64 workstation (CMUCL) which I access over our
RW> intranet. It's more comfortable to do development on the PC
RW> and do the long runs on the Compaq machine. I'm going to
RW> install Linux on the PC too (not so easy to get it through in
RW> the the institute I'm working at).

Given the above, you might want to consider running an X server on your
PC and using the Compaq exclusively. I once thought this would be obvious
but it seems many people do not know about this possibility.

[...]
RW> I tried ACL6 on the PC and CMUCL on the Compaq machine. I used
RW> declarations (I asked in this newsgroup how to do it best). [...]

CMUCL manual and especially the CMUCL compiler efficiency notes should
help here. Try bumping the efficiency note threshold to see all the
details of what the compiler is trying. It might also be wothwhile to
try to see if inlining your fuction helps. Kent's suggestion with the
optional arguments + inlining might produce tight code in CMUCL.

Some of us might be motivated to try your code and help further if you
promise to do a write-up (maybe for the cookbook?) afterwards.

cheers,

BM

Erik Naggum

unread,
Mar 10, 2002, 2:54:35 PM3/10/02
to
* Rolf Wester <rolf....@t-online.de>

| This would be true for you but not necessaryly for a lisp beginner like
| me. At present this is a problem for me. I know C++ well enough to be
| able to do my work in a reasonable or at least predictable time. At
| present I don't have the same confidence in my Lisp skills.

In that case, it is encouraging that you try Common Lisp. (I started to
use Common Lisp because C++ was just way too painful, and in the learning
period, sustained myself as a journalist/columnist just to get away from
programming computers for pay for a few years. It was that bad. :)

| I guess I should learn about IA32 assembler.

If you do, what you really need is a macro language that _works_, so
producing assembly code using Common Lisp is the only way to go.

| I read about compiler-maros before but have no clear understanding what
| this is. Can you give me a link to somewhere I can learn more about it?

Not really. They can be used to turn a function call into a macro call,
such that the call is effectively expanded inline. Suppose that your
maxwell-v-c1-c2 function does (* c1 v v (exp (* c2 v v ))), and that
calling a function with three arguments would lose the type information
the compiler is able to infer or has to been taught by you, and might
even have to generic floating-point operations. If you had this function
and decided that it would make sense to optimize this call, you could do

(defun maxwell-v-c1-c2 (v c1 c2)
(declare (double-float v c1 c2)
(optimize (speed 3) (safety 0) (debug 0)))
(* c1 v v (exp (* c2 v v))))

(defun maxwell-caller (v temp m)
(declare (double-float v temp m)
(optimize (speed 3) (safety 0) (debug 0)))


(let ((c1 (* 4.0d0 dpi (expt (/ m (* 2.0d0 dpi +kB+ temp)) 1.5d0)))
(c2 (/ (* -1.0d0 m) (* 2.0d0 +kB+ temp))))

(maxwell-v-c1-c2 v c1 c2)))

This would need to box the double-float arguments, but could benefit from
the fast, pure assembly code that is in the inner function.

(define-compiler-macro maxwell-v-c1-c2 (v c1 c2)
`(* c1 v v (exp (* c2 v v))))

This is an example of how to use compiler macros to inline user functions
such that type inference and declarations still work. Some Common Lisp
environments provide that functionality with a declaration to inline a
particular function, but that is neither necessarily portable nor
actually as effective.

The biggest advantage of a compiler macro, however, is how it may be used
to change a function call into something else. Suppose you have a
function that takes any number of parameters, but it has a simple
definition for, say, 0, 1, and 2 arguments, like + does.

(define-compiler-macro + (&whole form
&optional (arg-1 nil arg-1-p)
(arg-2 nil arg-2-p)
&rest arguments)
(cond ((not arg-1-p) 0)
((not arg-2-p) arg-1)
((not arguments) `(internal-2-arg-+ arg-1 arg-2))
(t form)))

A macro cannot decline to expand, but a compiler-macro can do just that
by returning the whole form unchanged.

This may or may not help...

Kenny Tilton

unread,
Mar 10, 2002, 4:12:30 PM3/10/02
to

Rolf Wester wrote:
>
> The point was I wanted to know how I can do calculations at macro expansion
> time not only for the problem at hand:
> (defmacro mm (name a)
> (let ((b (* 2 a)))
> `(defun ,name (c)
> (* b c))))
> so that:
> (mm f 2)
> expands to:
> (defun f (c)
> (* 4 c))

It's been a long thread so maybe I missed it, but the key is, as you
say, what is known at macro-expansion time. So MM works if the second
argument is always a number, and you make it (* ,b c):

(defmacro mm (name a)
(let ((b (* 2 a)))
`(defun ,name (c)

(* ,b c))))

I do not know why others recommended not using macro, but my problem
with that was it looked as if you did not know any of the arguments at
macro-expansion time.

an unrelated thought: supposing for the sake of argument that
declarations and optimization settings don't get you the performance of
some other language X, hell, use UFFI to call X:

http://uffi.med-info.com

if perchance the fastest X is assembler, I say go for it. Just doing a
little aritihmetic is a nice finite "hello,world" to get started with.

But given the price of systems today, I like Erik's idea of harnessing
multiple CPUs.


--

kenny tilton
clinisys, inc
---------------------------------------------------------------
"Be the ball...be the ball...you're not being the ball, Danny."
- Ty, Caddy Shack

Rolf Wester

unread,
Mar 11, 2002, 3:29:06 AM3/11/02
to
Kenny Tilton wrote:

> Rolf Wester wrote:
> >
> > The point was I wanted to know how I can do calculations at macro expansion
> > time not only for the problem at hand:
> > (defmacro mm (name a)
> > (let ((b (* 2 a)))
> > `(defun ,name (c)
> > (* b c))))
> > so that:
> > (mm f 2)
> > expands to:
> > (defun f (c)
> > (* 4 c))
>
> It's been a long thread so maybe I missed it, but the key is, as you
> say, what is known at macro-expansion time. So MM works if the second
> argument is always a number, and you make it (* ,b c):
>
> (defmacro mm (name a)
> (let ((b (* 2 a)))
> `(defun ,name (c)
> (* ,b c))))
>
> I do not know why others recommended not using macro, but my problem
> with that was it looked as if you did not know any of the arguments at
> macro-expansion time.

Originally I supposed that a is known at macro-expansion time. But this of course

is a severe restriction which might not be useful for my real problem. So a
closure
or some kind of caching is the more flexible solution

> an unrelated thought: supposing for the sake of argument that
> declarations and optimization settings don't get you the performance of
> some other language X, hell, use UFFI to call X:
>
> http://uffi.med-info.com
>
> if perchance the fastest X is assembler, I say go for it. Just doing a
> little aritihmetic is a nice finite "hello,world" to get started with.
>

The problem at present is that CMUCL's load-foreign doesn't work with the
binary Compaq Alpha Tru64 distribution. I will try to compile CMUCL
form sources which I read is not an easy thing to do.

> But given the price of systems today, I like Erik's idea of harnessing
> multiple CPUs.

We have two fast Compaq machines at present (and some older ones). But I guess
for number crunching these two will be the last ones we byed. x86/Linux is a much

cheaper alternative.

Thanks for your reply.

Regards

Rolf Wester

Rolf Wester

unread,
Mar 11, 2002, 3:48:11 AM3/11/02
to

Thank you, it helped to get an idea about compiler-macros.

Regards

Rolf Wester


Rolf Wester

unread,
Mar 11, 2002, 4:00:27 AM3/11/02
to

Bulent Murtezaoglu schrieb:

> >>>>> "RW" == Rolf Wester <rolf....@t-online.de> writes:
> [...]
> RW> The point is that at work I have a NT PC (CLISP) and a Compaq
> RW> Tru64 workstation (CMUCL) which I access over our
> RW> intranet. It's more comfortable to do development on the PC
> RW> and do the long runs on the Compaq machine. I'm going to
> RW> install Linux on the PC too (not so easy to get it through in
> RW> the the institute I'm working at).
>
> Given the above, you might want to consider running an X server on your
> PC and using the Compaq exclusively. I once thought this would be obvious
> but it seems many people do not know about this possibility.

I forgot to mention that, I'm running eXcursion on the PC. But this is still
not very
comfortable because I don't have all the nice tools that are available under
Linux.
I have installed some of these toos on the UNIX machine but this doesn't work
in any case.

> [...]
> RW> I tried ACL6 on the PC and CMUCL on the Compaq machine. I used
> RW> declarations (I asked in this newsgroup how to do it best). [...]
>
> CMUCL manual and especially the CMUCL compiler efficiency notes should
> help here. Try bumping the efficiency note threshold to see all the
> details of what the compiler is trying. It might also be wothwhile to
> try to see if inlining your fuction helps. Kent's suggestion with the
> optional arguments + inlining might produce tight code in CMUCL.
>

The CMUCL manual isn't very comprehensive, so it's not so easy for me to
understand all the compiler messages.

>
> Some of us might be motivated to try your code and help further if you
> promise to do a write-up (maybe for the cookbook?) afterwards.
>

I will go on in using CMUCL and there will probably be some more problems
for which I will need the help of this newsgroup. And I would not mind to
contribute
to the cookbook. I will try to find time to compile all the solutions
suggested in this thread.

Regards

Rolf Wester

Bulent Murtezaoglu

unread,
Mar 11, 2002, 12:22:12 PM3/11/02
to
>>>>> "RW" == Rolf Wester <wes...@ilt.fhg.de> writes:
[...]
RW> I forgot to mention that, I'm running eXcursion on the PC.

It seems I consistently guess wrong on this! Sorry.

RW> But
RW> this is still not very comfortable because I don't have all
RW> the nice tools that are available under Linux. [...]

Ilisp+emacs should work OK under Tru64, AFAIK. Linux certainly has
more software available for it, but in your case I don't know if much
is necessary beyond ilisp+emacs. I bring this up because having a
decent development environment makes life easier and it seemed that
you already had most of what you needed w/o installing another OS.

[...]
RW> The CMUCL manual isn't very comprehensive, so it's not so easy
RW> for me to understand all the compiler messages. [...]

Yes, last I checked the efficiency notes were not documented in detail
in the manual and understanding them does require some background which
the manual probably assumes the reader has. It is a decent manual, but
given your comment above maybe additional documentation geared towards
a broader audience would be helpful. If sombody were to ask questions
about individual efficiency notes maybe more accessible documentation
could be generated from the answers filling in the details that long
time lisp users take for granted.

cheers,

BM

Thomas F. Burdick

unread,
Mar 11, 2002, 3:53:49 PM3/11/02
to
Rolf Wester <wes...@ilt.fhg.de> writes:

> Bulent Murtezaoglu schrieb:
>
> > >>>>> "RW" == Rolf Wester <rolf....@t-online.de> writes:
> > [...]
> > RW> The point is that at work I have a NT PC (CLISP) and a Compaq
> > RW> Tru64 workstation (CMUCL) which I access over our
> > RW> intranet. It's more comfortable to do development on the PC
> > RW> and do the long runs on the Compaq machine. I'm going to
> > RW> install Linux on the PC too (not so easy to get it through in
> > RW> the the institute I'm working at).
> >
> > Given the above, you might want to consider running an X server on your
> > PC and using the Compaq exclusively. I once thought this would be obvious
> > but it seems many people do not know about this possibility.
>
> I forgot to mention that, I'm running eXcursion on the PC. But this
> is still not very comfortable because I don't have all the nice
> tools that are available under Linux. I have installed some of
> these toos on the UNIX machine but this doesn't work in any case.

? Like what are you missing? When hacking Lisp, I spend so much time
in the Lisp image, that I can hardly tell Solaris from Linux unless I
call disassemble.

> > [...]
> > RW> I tried ACL6 on the PC and CMUCL on the Compaq machine. I used
> > RW> declarations (I asked in this newsgroup how to do it best). [...]
> >
> > CMUCL manual and especially the CMUCL compiler efficiency notes should
> > help here. Try bumping the efficiency note threshold to see all the
> > details of what the compiler is trying. It might also be wothwhile to
> > try to see if inlining your fuction helps. Kent's suggestion with the
> > optional arguments + inlining might produce tight code in CMUCL.
>
> The CMUCL manual isn't very comprehensive, so it's not so easy for me to
> understand all the compiler messages.
>
> > Some of us might be motivated to try your code and help further if you
> > promise to do a write-up (maybe for the cookbook?) afterwards.
>
> I will go on in using CMUCL and there will probably be some more
> problems for which I will need the help of this newsgroup. And I
> would not mind to contribute to the cookbook. I will try to find
> time to compile all the solutions suggested in this thread.

Don't forget the cmucl-help list, too.

Rolf Wester

unread,
Mar 12, 2002, 4:49:39 AM3/12/02
to

Bulent Murtezaoglu wrote:

> RW> But
> RW> this is still not very comfortable because I don't have all
> RW> the nice tools that are available under Linux. [...]
>
> Ilisp+emacs should work OK under Tru64, AFAIK. Linux certainly has
> more software available for it, but in your case I don't know if much
> is necessary beyond ilisp+emacs. I bring this up because having a
> decent development environment makes life easier and it seemed that
> you already had most of what you needed w/o installing another OS.
>

XEmacs works (almost fine). I have tried Ilisp but wasn't able to figure
out exactly
how it works. It couldn't find documentation about Ilisp that could help
me. But I will
try it again and spend some more time on it.
The problem with the Compaq machine is that not every software that
compiles without
problem under Linux (I have Linux installed on my private PC where I can
try things out).
compiles under Tru64 (CLISP for example didn't work) and the binary CMUCL
for
Tru64 isn't as complete as the Linux version (CMUCL18b, no load-foreign, no
threads).

>
> [...]
> RW> The CMUCL manual isn't very comprehensive, so it's not so easy
> RW> for me to understand all the compiler messages. [...]
>
> Yes, last I checked the efficiency notes were not documented in detail
> in the manual and understanding them does require some background which
> the manual probably assumes the reader has. It is a decent manual, but
> given your comment above maybe additional documentation geared towards
> a broader audience would be helpful. If sombody were to ask questions
> about individual efficiency notes maybe more accessible documentation
> could be generated from the answers filling in the details that long
> time lisp users take for granted.
>

Thanks for your hints.

Regards

Rolf Wester

Rolf Wester

unread,
Mar 12, 2002, 4:53:48 AM3/12/02
to

"Thomas F. Burdick" wrote:

> > I forgot to mention that, I'm running eXcursion on the PC. But this
> > is still not very comfortable because I don't have all the nice
> > tools that are available under Linux. I have installed some of
> > these toos on the UNIX machine but this doesn't work in any case.
>
> ? Like what are you missing? When hacking Lisp, I spend so much time
> in the Lisp image, that I can hardly tell Solaris from Linux unless I
> call disassemble.

For example the binary True64 CMUCL version is only 18b without load-foreign
and without threads (if I'm not wrong).

> > I will go on in using CMUCL and there will probably be some more
> > problems for which I will need the help of this newsgroup. And I
> > would not mind to contribute to the cookbook. I will try to find
> > time to compile all the solutions suggested in this thread.
>
> Don't forget the cmucl-help list, too.

I will not forget it.

Regards

Rolf Wester

Jacek Generowicz

unread,
Mar 12, 2002, 5:54:33 AM3/12/02
to
Rolf Wester <wes...@ilt.fhg.de> writes:

> Bulent Murtezaoglu wrote:
>
> > RW> But
> > RW> this is still not very comfortable because I don't have all
> > RW> the nice tools that are available under Linux. [...]
> >
> > Ilisp+emacs should work OK under Tru64, AFAIK. Linux certainly has
> > more software available for it, but in your case I don't know if much
> > is necessary beyond ilisp+emacs. I bring this up because having a
> > decent development environment makes life easier and it seemed that
> > you already had most of what you needed w/o installing another OS.
> >
>
> XEmacs works (almost fine). I have tried Ilisp but wasn't able to
> figure out exactly how it works. It couldn't find documentation
> about Ilisp that could help me.

If you've got as far as installing it sufficiently correctly for M-x
run-ilisp to start up the dialect you choose, then C-h m (or
M-x describe-mode) in the relevant buffer should get you going.

If you have easymenu, and set up your ilisp for it, then just look in
the ilisp menu, where you will find the most commonly used features
(and the keybindings which invoke them).

Otherwise, I'd probably recommend starting by checking out

C-c n
C-c r
C-c c
C-c w
C-c * e
C-c * c

> But I will try it again and spend some more time on it.

I think you will find this well worth your while.

Rolf Wester

unread,
Mar 12, 2002, 11:21:13 AM3/12/02
to

Jacek Generowicz writes:

> Rolf Wester <wes...@ilt.fhg.de> writes:
>
> >
> > XEmacs works (almost fine). I have tried Ilisp but wasn't able to
> > figure out exactly how it works. It couldn't find documentation
> > about Ilisp that could help me.
>
> If you've got as far as installing it sufficiently correctly for M-x
> run-ilisp to start up the dialect you choose, then C-h m (or
> M-x describe-mode) in the relevant buffer should get you going.
>
> If you have easymenu, and set up your ilisp for it, then just look in
> the ilisp menu, where you will find the most commonly used features
> (and the keybindings which invoke them).
>
> Otherwise, I'd probably recommend starting by checking out
>
> C-c n
> C-c r
> C-c c
> C-c w
> C-c * e
> C-c * c
>

Thanks a lot for that information. I'll try it.

Regards

Rolf Wester

0 new messages