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.

(incf x) slower than (setf x (+ x 1))?

6 posts · 1784 views

I'm testing in GNU CLISP. I havent tried sbcl or others (yet)
Without testing I have used incf and decf since I htough it would be a optimization. Today I benchmarked a little and found out incf performes 3 times slower than setf.
For me this doesn't make any sense. Why do we have incf/decf if not for speed?

[7]> (time (dotimes (n 10000) (setf test (+ 1 test))))
Real time: 0.055758 sec.
Run time: 0.056004 sec.
Space: 400840 Bytes
GC: 1, GC time: 0.012 sec.
NIL
[8]> (time (dotimes (n 10000) (setf test (1+ test))))
Real time: 0.042886 sec.
Run time: 0.040002 sec.
Space: 400840 Bytes
NIL
[9]> (time (dotimes (n 10000) (incf test 1)))
Real time: 0.152384 sec.
Run time: 0.152009 sec.
Space: 3040840 Bytes
GC: 4, GC time: 0.028002 sec.
NIL
I'm the author of two useless languages that uses BF as target machine.
Currently I'm planning a Scheme compiler :p

Re: (incf x) slower than (setf x (+ x 1))?

sylwester wrote:I'm testing in GNU CLISP. I havent tried sbcl or others (yet)
Without testing I have used incf and decf since I htough it would be a optimization. Today I benchmarked a little and found out incf performes 3 times slower than setf.
For me this doesn't make any sense. Why do we have incf/decf if not for speed?[/code]
We don't have it for speed. INCF wraps SETF, to put it simply, and I'd have to figure that SETF compiles more or less directly to a MOV opcode anyway, so you're not going to top SETF for efficiency. Rather, INCF exists for convenience. I'm guessing that INCF is slower in CLISP because CLISP has to do so much consing... That is what it means by "space" isn't it?

You might find the output of these expressions interesting:

CL-USER> (let ((bork 0)) (disassemble #'(lambda () (setf bork (1+ bork)))))
CL-USER> (let ((bork 0)) (disassemble #'(lambda () (incf bork))))

Also: define-modify-macro
"If you want to improve, be content to be thought foolish and stupid." -Epictetus

Re: (incf x) slower than (setf x (+ x 1))?

sylwester wrote:Why do we have incf/decf if not for speed?
For code readability/conciseness?

Try writing the 'setf' equivalent to
  • (incf (aref myarray (incf indx)))

Re: (incf x) slower than (setf x (+ x 1))?

sylwester wrote:For me this doesn't make any sense. Why do we have incf/decf if not for speed?
CLISP is an interpreter, and when executed from the REPL the expressions appears to be fully interpreted, that is, INCF is expanded inside the loop, which obviously costs time and space. If you wrap the operations in a function the time difference disappears, and you can see using DISASSEMBLE that they emit the same code.

Re: (incf x) slower than (setf x (+ x 1))?

So I was fooled by the interprenter repl. Since I prefer incf I'll continue with it and for the future I'll make compiled functions out of my benchmarks :)
I'm the author of two useless languages that uses BF as target machine.
Currently I'm planning a Scheme compiler :p

Re: (incf x) slower than (setf x (+ x 1))?

In compiled code, there is no difference:
[8]> (defun f () (let ((test 0)) (dotimes (i 10000) (incf test))))
f
[9]> (defun g () (let ((test 0)) (dotimes (i 10000) (setf test (+ test 1)))))
g
[10]> (compile 'f)
f ;
nil ;
nil
[11]> (compile 'g)
g ;
nil ;
nil
[12]> (time (f))
Real time: 0.001149 sec.
Run time: 0.0 sec.
Space: 0 Bytes
nil
[13]> (time (f))
Real time: 0.001147 sec.
Run time: 0.004001 sec.
Space: 0 Bytes
nil
[14]> (time (g))
Real time: 0.00115 sec.
Run time: 0.004 sec.
Space: 0 Bytes
nil
[15]> (time (g))
Real time: 0.001142 sec.
Run time: 0.004 sec.
Space: 0 Bytes
nil
Indeed, (incf x) expands into a setf form (actually setq, but setf also expands into a setq):
[16]> (macroexpand '(incf x))
(setq x (+ x 1)) ;
t
[17]> (macroexpand '(setf x (+ x 1)))
(setq x (+ x 1)) ;
t