Using some of the tips in @sds's answer, I realized that there are simple preliminary tests that might answer my question for a specific implementation (without having to work through dissemble
output). It appears that SBCL and CCL do not necessarily treat mapcar
the same way as mapc
when mapcar
's return value is ignored. First define lis1
and lis2
as lists of significant length (mine were length 100,000, and contained integers), then run mapcar
, mapc
, etc. over them many times, as in the the following (with optional preliminary calls to gc
to clear out old garbage):
(gc :full t)
(time
(progn
(dotimes (ignored 1000)
(mapcar #'+ lis1 lis2))
(format t "mapcar:~%")))
(gc :full t)
(time
(progn
(dotimes (ignored 1000)
(mapc #'+ lis1 lis2))
(format t "mapc:~%")))
(gc :full t)
(time
(progn
(dotimes (ignored 1000)
(map nil #'+ (the list lis1) (the list lis2)))
(format t "map nil with lists~%")))
For example, SBCL on my machine produces:
mapcar:
Evaluation took:
2.306 seconds of real time
2.287627 seconds of total run time (2.136130 user, 0.151497 system)
[ Run times consist of 0.147 seconds GC time, and 2.141 seconds non-GC time. ]
99.22% CPU
3,683,188,504 processor cycles
1,600,049,536 bytes consed
mapc:
Evaluation took:
0.639 seconds of real time
0.638733 seconds of total run time (0.638011 user, 0.000722 system)
100.00% CPU
1,020,310,296 processor cycles
0 bytes consed
map nil with lists
Evaluation took:
0.592 seconds of real time
0.592114 seconds of total run time (0.591199 user, 0.000915 system)
100.00% CPU
945,957,944 processor cycles
0 bytes consed
These are typical results with default optimization settings. Using declaim
to optimize for speed, non-safety, etc. speeds things up a little bit but doesn't change the fact that mapc
and map nil
are an order of magnitude faster than mapcar
, and that mapcar
does a lot of consing. Results for CCL are similar, but slower overall.