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

Discussion of Common Lisp
Post Reply
sylwester
Posts: 133
Joined: Mon Jul 11, 2011 2:53 pm

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

Post by sylwester » Fri Aug 19, 2011 5:46 pm

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: Select all

[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

Duke
Posts: 38
Joined: Sat Oct 17, 2009 10:40 pm
Contact:

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

Post by Duke » Fri Aug 19, 2011 6:07 pm

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

saulgoode
Posts: 45
Joined: Tue Dec 14, 2010 1:39 am

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

Post by saulgoode » Fri Aug 19, 2011 6:26 pm

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)))

ramarren
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland
Contact:

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

Post by ramarren » Sat Aug 20, 2011 12:16 am

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.

sylwester
Posts: 133
Joined: Mon Jul 11, 2011 2:53 pm

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

Post by sylwester » Sun Aug 21, 2011 2:12 pm

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

huangho
Posts: 3
Joined: Fri Sep 03, 2010 5:15 pm
Location: Porto Alegre, Brazil
Contact:

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

Post by huangho » Mon Aug 22, 2011 4:09 pm

In compiled code, there is no difference:

Code: Select all

[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):

Code: Select all

[16]> (macroexpand '(incf x))
(setq x (+ x 1)) ;
t
[17]> (macroexpand '(setf x (+ x 1)))
(setq x (+ x 1)) ;
t

Post Reply