This is a read-only archive of lispforum.com. The forum was locked to new users and posts and is preserved here as static HTML from a database snapshot taken on 2019-09-07.

What does clisp do behind the scene if you give it (* 2 2 2)

11 posts · 2884 views

you can write (* (* 2 2) 2) or you can write (* 2 2 2)...

I am wondering what is happening behind the scenes when comparing the two.

If (* 2 2 2) is somehow converted to (* (* 2 2) 2), then that extra step used in the conversion would mean that technically it would take slightly longer for the CPU to compute right?

Re: What does clisp do behind the scene if you give it (* 2 2 2)

just to add, I realize that operators like * are n-ary operators... but I am wondering specifically which one is "faster", or more efficient, aside from the argument that one is "easier to read" then the other...

Re: What does clisp do behind the scene if you give it (* 2 2 2)

Don't worry about efficiency until you have a large program that runs too slowly...

A good compiler should produce the same speed code for (* x y z) and (* x (* y z)). Thus feel free to use whichever form looks better.

Re: What does clisp do behind the scene if you give it (* 2 2 2)

well actually the reason I am wondering is not because I am worried about efficiency, its more of an argument. I lost marks on an assignment (an entire letter grade) because "there is a simpler solution"...

The question said: "compute 2^3"...

Anyways, I am arguing that his definition of what "simpler" is is rather ambiguous. does simpler mean an answer with the least characters? Because by that logic a much much simpler solution would have been:
>'8

or does simpler mean speed, or memory efficiency? If both answers are equivalent after the compiler has its way, then its a matter of style... not the solution itself.

Actually for the newcomer to LISP, I think my way makes more sense because in order to understand (* 2 2 2 2 2 2 2 2) you need to understand the theory of something like the reduce function. But (* (*2 2) 2) is rather easily understood after reading the first chapter of "Common LISPcraft". (* 2 2) is evaluated, then 2 is evaluated, then the outside is evaluated. It is easy to say (* 2 2 2) works, but after only reading chapter 1 it doesn't tie in with the basics.

Re: What does clisp do behind the scene if you give it (* 2 2 2)

yesimthetaxman wrote:If (* 2 2 2) is somehow converted to (* (* 2 2) 2), then that extra step used in the conversion would mean that technically it would take slightly longer for the CPU to compute right?
In SBCL, it actually does take a single extra operation to do (* a (* b c)) as compared to (* a b c). I'd assume CLISP adds at least as much overhead, given that SBCL is considered "better optimized" in general, but that's probably not what is meant by "simpler" in this context.

I'm not sure what IS meant by it, but maybe you were supposed to do an exponentiation function? That would be the smart answer, simplicity be damned. ;)
"If you want to improve, be content to be thought foolish and stupid." -Epictetus

Re: What does clisp do behind the scene if you give it (* 2 2 2)

Duke wrote:
yesimthetaxman wrote:If (* 2 2 2) is somehow converted to (* (* 2 2) 2), then that extra step used in the conversion would mean that technically it would take slightly longer for the CPU to compute right?
In SBCL, it actually does take a single extra operation to do (* a (* b c)) as compared to (* a b c). I'd assume CLISP adds at least as much overhead, given that SBCL is considered "better optimized" in general, but that's probably not what is meant by "simpler" in this context.

I'm not sure what IS meant by it, but maybe you were supposed to do an exponentiation function? That would be the smart answer, simplicity be damned. ;)

Well I don't think we were meant to do an exponentiation function because the posted solution was (* 2 2 2)... Basically the first lecture was administrative stuff, and the second lecture started with the basics of LISP, which was when we were given the assignment, which as you can see was relatively simple. I think that after only reading the basics of LISP, I was justified in assuming that (* 2 2 2) had more overhead then (* (* 2 2) 2), because I assumed that (* 2 2 2) had to be truncated somehow to (* (* 2 2) 2)... although now I know better ;)

Re: What does clisp do behind the scene if you give it (* 2 2 2)

yesimthetaxman wrote:Anyways, I am arguing that his definition of what "simpler" is is rather ambiguous. does simpler mean an answer with the least characters? Because by that logic a much much simpler solution would have been:
>'8

or does simpler mean speed, or memory efficiency? If both answers are equivalent after the compiler has its way, then its a matter of style... not the solution itself.
My guess is that what was intended by "simpler" was the syntax trees representing the two expressions (that is, how the expressions were stored after reading, not worrying about eval at all).

The syntax tree for (* 2 2 2) would be (with 'v' representing a pointer/reference):
[ * . v ]
    [ 2 . v ]
        [ 2 . v ]
            [ 2 . nil ]    
Whereas (* (* 2 2) 2) would be:
[ * . v ]
    [ v         .           v ]
    [ * . v ]             [ 2 . nil]
        [ 2 . v ]
            [ 2 . nil]

Re: What does clisp do behind the scene if you give it (* 2 2 2)

Duke wrote:In SBCL, it actually does take a single extra operation to do (* a (* b c)) as compared to (* a b c).
That is not the case if the code is compiled.

Re: What does clisp do behind the scene if you give it (* 2 2 2)

gugamilare wrote:
Duke wrote:In SBCL, it actually does take a single extra operation to do (* a (* b c)) as compared to (* a b c).
That is not the case if the code is compiled.
This is off topic, but... when is the code ever not compiled in SBCL? Is there a circumstance where compiled-function-p returns nil?
"If you want to improve, be content to be thought foolish and stupid." -Epictetus

Re: What does clisp do behind the scene if you give it (* 2 2 2)

Duke wrote:This is off topic, but... when is the code ever not compiled in SBCL? Is there a circumstance where compiled-function-p returns nil?
I know the REPL is evaluated, not compiled, unless SBCL finds something it thinks it is better to compile. There is a way of changing SBCL to "evaluator mode" (I don't remember how) in which it does not compile anything unless explicitly told to, and, in this case, no function in the REPL is compiled.

Re: What does clisp do behind the scene if you give it (* 2 2 2)

yesimthetaxman wrote:If (* 2 2 2) is somehow converted to (* (* 2 2) 2), then that extra step used in the conversion would mean that technically it would take slightly longer for the CPU to compute right?
That's not what happens:
[1]> (disassemble (lambda (x y z) (* x y z)))

Disassembly of function :LAMBDA
...
5 byte-code instructions:
0     (LOAD&PUSH 3)
1     (LOAD&PUSH 3)
2     (LOAD&PUSH 3)
3     (CALLSR 3 57)                       ; *
6     (SKIP&RET 4)
NIL
[2]> (disassemble (lambda (x y z) (* (* x y) z)))

Disassembly of function :LAMBDA
...
6 byte-code instructions:
0     (LOAD&PUSH 3)
1     (LOAD&PUSH 3)
2     (CALLSR&PUSH 2 57)                  ; *
5     (LOAD&PUSH 2)
6     (CALLSR 2 57)                       ; *
9     (SKIP&RET 4)
NIL
Now, what does (CALLSR 3 57) do? Let's have a look at the FUNTABR structure in eval.d. The 57th entry, along with the comment above it, tells us that we're looking for the star function in lisparit.d. Its implementation is:
LISPFUN(star,seclass_foldable,0,0,rest,nokey,0,NIL)
{ /* (* {number}), CLTL p. 199
     method:
     (*) = 1
     (* x1 x2 x3 ... xn) = (* ...(* (* x1 x2) x3)... xn) */
  if (argcount==0) {
    VALUES1(Fixnum_1); return;
  }
  argcount--;
  test_number_args(argcount,rest_args_pointer); /* all arguments numbers? */
  /* method:
     n+1 arguments Arg[0..n].
     x:=Arg[0], for i:=1 to n do ( x := x*Arg[i] ), return(x). */
  var gcv_object_t* arg_i_ptr = rest_args_pointer;
  var object x = NEXT(arg_i_ptr); /* product so far */
  dotimesC(argcount,argcount, {
    var object arg = NEXT(arg_i_ptr);
    x = (eq(x,arg) ? N_square_N(x) : N_N_mult_N(x,arg));
  });
  VALUES1(x); set_args_end_pointer(rest_args_pointer);
}
I'm not an expert in CLISP internals, but I would naïvely guess that a single dotimesC loop is probably slightly more efficient than having to call the star function twice.