-
Notifications
You must be signed in to change notification settings - Fork 3
Speed Comparisons
NOTE: As Loopy develops, these examples and results might become out of
date. For the most up-to-date information, refer to this package’s
documentation, which is installed with the package as the Info file loopy
.
Additionally, this information can/should probably be expanded to compare to several other approaches.
This file contains benchmarks comparing cl-loop
, loopy
, some of the built-in
C functions like mapcar
and mapcan
, some of CL Lib’s functions like
cl-mapcar
and cl-mapcan
, some of Dash’s functions like -map
and
-mapcat
, and some Seq functions like seq-reduce
.
In general, for simple cases, they can be sorted from fastest to slowest as
- C functions like
mapcar
andapply
-
loopy
andcl-loop
(as far as these benchmarks cover) -
-map
(but not-count
) - Seq’s functions
The C functions are fastest because they are in C, and Dash’s functions are
should be faster because they try to be pure and side-effect free (which allows
for better optimizations). The Dash results were better when testing on Emacs
27, but both loopy
and cl-loop
closed the gap (and in several cases passed)
when testing with Emacs 28 with native compilation.
For straightforward cases, loopy
and cl-loop
are overkill. They allow a lot
more flexibility with branch code in loops, but they can’t be the C functions in
terms of speed.
One important (for these tests) difference between loopy
and cl-loop
, is
that, in loopy
, all iteration variables are bound to nil
until their
respective iteration command is run. This consistency is deemed useful for
predictability, but can require extra variables and assignments, making it
slower and not directly comparable to what cl-loop
is doing. Regardless,
loopy
is usually about the same speed if not faster.
loopy
tries to follow the same logic as cl-loop
, but in being more
flexible in some cases, it cannot always implement concepts as efficiently as
cl-loop
. However, for general cases, they should be more or less the same.
These comparisons are made using macros listed in user Alphapapa’s useful Emacs Package Dev Handbook. For completeness, they are copied below.
(require 'cl-lib)
;;;###autoload
(cl-defmacro bench (&optional (times 100000) &rest body)
"Call `benchmark-run-compiled' on BODY with TIMES iterations, returning list suitable for Org source block evaluation.
Garbage is collected before calling `benchmark-run-compiled' to
avoid counting existing garbage which needs collection."
(declare (indent defun))
`(progn
(garbage-collect)
(list '("Total runtime" "# of GCs" "Total GC runtime")
'hline
(benchmark-run-compiled ,times
(progn
,@body)))))
;;;###autoload
(cl-defmacro bench-multi (&key (times 1) forms ensure-equal raw)
"Return Org table as a list with benchmark results for FORMS.
Runs FORMS with `benchmark-run-compiled' for TIMES iterations.
When ENSURE-EQUAL is non-nil, the results of FORMS are compared,
and an error is raised if they aren't `equal'. If the results are
sequences, the difference between them is shown with
`seq-difference'.
When RAW is non-nil, the raw results from
`benchmark-run-compiled' are returned instead of an Org table
list.
If the first element of a form is a string, it's used as the
form's description in the bench-multi-results; otherwise, forms
are numbered from 0.
Before each form is run, `garbage-collect' is called."
;; MAYBE: Since `bench-multi-lexical' byte-compiles the file, I'm not sure if
;; `benchmark-run-compiled' is necessary over `benchmark-run', or if it matters.
(declare (indent defun))
(let*((keys (gensym "keys"))
(result-times (gensym "result-times"))
(header '(("Form" "x faster than next" "Total runtime" "# of GCs" "Total GC runtime")
hline))
;; Copy forms so that a subsequent call of the macro will get the original forms.
(forms (cl-copy-list forms))
(descriptions (cl-loop for form in forms
for i from 0
collect (if (stringp (car form))
(prog1 (car form)
(setf (nth i forms) (cadr (nth i forms))))
i))))
`(unwind-protect
(progn
(defvar bench-multi-results nil)
(let* ((bench-multi-results (make-hash-table))
(,result-times (sort (list ,@(cl-loop for form in forms
for i from 0
for description = (nth i descriptions)
collect `(progn
(garbage-collect)
(cons ,description
(benchmark-run-compiled ,times
,(if ensure-equal
`(puthash ,description ,form bench-multi-results)
form))))))
(lambda (a b)
(< (cl-second a) (cl-second b))))))
,(when ensure-equal
`(cl-loop with ,keys = (hash-table-keys bench-multi-results)
for i from 0 to (- (length ,keys) 2)
unless (equal (gethash (nth i ,keys) bench-multi-results)
(gethash (nth (1+ i) ,keys) bench-multi-results))
do (if (sequencep (gethash (car (hash-table-keys bench-multi-results)) bench-multi-results))
(let* ((k1) (k2)
;; If the difference in one order is nil, try in other order.
(difference (or (setq k1 (nth i ,keys)
k2 (nth (1+ i) ,keys)
difference (seq-difference (gethash k1 bench-multi-results)
(gethash k2 bench-multi-results)))
(setq k1 (nth (1+ i) ,keys)
k2 (nth i ,keys)
difference (seq-difference (gethash k1 bench-multi-results)
(gethash k2 bench-multi-results))))))
(user-error "Forms' bench-multi-results not equal: difference (%s - %s): %S"
k1 k2 difference))
;; Not a sequence
(user-error "Forms' bench-multi-results not equal: %s:%S %s:%S"
(nth i ,keys) (nth (1+ i) ,keys)
(gethash (nth i ,keys) bench-multi-results)
(gethash (nth (1+ i) ,keys) bench-multi-results)))))
;; Add factors to times and return table
(if ,raw
,result-times
(append ',header
(bench-multi-process-results ,result-times)))))
(unintern 'bench-multi-results nil))))
(defun bench-multi-process-results (results)
"Return sorted RESULTS with factors added."
;; (setq results (sort results (-on #'< #'cl-second)))
(setq results (cl-sort results #'< :key #'cl-second))
(cl-loop with length = (length results)
for i from 0 to (1- length)
for description = (car (nth i results))
for factor = (if (< i (1- length))
(format "%.2f" (/ (cl-second (nth (1+ i) results))
(cl-second (nth i results))))
"slowest")
collect (append (list description factor)
(list (format "%.6f" (cl-second (nth i results)))
(cl-third (nth i results))
(if (> (cl-fourth (nth i results)) 0)
(format "%.6f" (cl-fourth (nth i results)))
0)))))
The following clauses don’t have exact equivalents in Loopy, but most can be
mimicked by using Emacs’s built-in functions and the list
loop command. See
the section For Clauses under Translating … for the relevant function.
for VAR being the symbols
for VAR being the hash-keys
for VAR being the hash-values
for VAR being the key-codes
for VAR being the key-bindings
for VAR being the key-bindings
for VAR being the key-seqs
for VAR being the overlays
for VAR being the intervals
for VAR being the frames
for VAR being the windows
for VAR being the buffers
-
for VAR from EXPR1 to EXPR2 by EXPR3
: When using the keywords (“CL style”), these expand to basically the same code. When using the positional arguments with variables (instead of printed numbers),loopy
has to check the direction at run time, which results in slower code viafuncall
.It seems faster to be able to use the numbers directly in the code instead of creating variables. In the table below, it is almost 69% slower to use variables.
Interestingly, even when running the same number of steps, it seems that stepping by 1 is faster than by other increments.
;; one thousand steps tested ten thousand times (bench-multi :times 10000 :forms (("cl-loop" (cl-loop for i from 1 to 1000)) ("loopy cl-style" (loopy (numbers i :from 1 :to 1000))) ("loopy python-style" (loopy (numbers i 1 1000))) ("cl-loop by 1" (cl-loop for i from 1 to 1000 by 1)) ("loopy cl-style by 1" (loopy (numbers i :from 1 :to 1000 :by 1))) ("loopy python-style by 1" (loopy (numbers i 1 1000 1))) ;; Tests other increment but same number of steps. ("cl-loop by 3" (cl-loop for i from 3 to 3000 by 3)) ("loopy cl-style by 3" (loopy (numbers i :from 3 :to 3000 :by 3))) ("loopy python-style by 3" (loopy (numbers i 3 3000 3))) ;; With variables. ("cl-loop with vars" (cl-loop with start = 1 and end = 1000 and step = 1 for i from start to end by step)) ("loopy cl-style with vars" (loopy (with (start 1) (end 1000) (step 1)) (numbers i :from start :to end :by step))) ("loopy python-style with vars" (loopy (with (start 1) (end 1000) (step 1)) (numbers i start end step)))))
Form x faster than next Total runtime # of GCs Total GC runtime loopy python-style 1.00 0.262468 0 0 cl-loop by 1 1.00 0.262500 0 0 loopy cl-style 1.00 0.262936 0 0 loopy cl-style by 1 1.00 0.263450 0 0 loopy python-style by 1 1.00 0.263473 0 0 cl-loop 1.49 0.263483 0 0 loopy python-style by 3 1.00 0.392483 0 0 loopy cl-style by 3 1.00 0.392819 0 0 cl-loop by 3 1.10 0.393508 0 0 cl-loop with vars 1.00 0.431381 0 0 loopy cl-style with vars 1.59 0.432137 0 0 loopy python-style with vars slowest 0.688223 0 0 -
for VAR in LIST by FUNCTION
:(let ((l (number-sequence 1 100))) (bench-multi :times 10000 :forms (("cl-loop" (cl-loop for i in l)) ("loopy" (loopy (list i l))) ;; These shouldn’t be different. ("cl-loop with function" (cl-loop for i in l by #'cdr)) ("loopy with function" (loopy (list i l :by #'cdr))))))
Form x faster than next Total runtime # of GCs Total GC runtime loopy with function 1.00 0.025758 0 0 cl-loop 1.01 0.025881 0 0 loopy 1.02 0.026019 0 0 cl-loop with function slowest 0.026655 0 0 -
for VAR on LIST by FUNCTION
: This has slightly different behavior.In
cl-loop
, the belowi
is bound to the value ofl
immediately, before the loop body is run.(let ((l (number-sequence 1 100))) (bench-multi :times 10000 :forms (("cl-loop" (cl-loop for i on l)) ("loopy" (loopy (cons i l))) ;; These shouldn’t be different. ("cl-loop with function" (cl-loop for i on l by #'cdr)) ("loopy with function" (loopy (cons i l :by #'cdr))))))
Form x faster than next Total runtime # of GCs Total GC runtime cl-loop 1.01 0.017197 0 0 cl-loop with function 1.40 0.017314 0 0 loopy 1.01 0.024299 0 0 loopy with function slowest 0.024485 0 0 -
for VAR in-ref LIST by FUNCTION
:(let ((l (number-sequence 1 100))) (bench-multi :times 10000 :forms (("cl-loop" (cl-loop for i in-ref l)) ("loopy" (loopy (list-ref i l))) ;; These shouldn’t be different. ("cl-loop with function" (cl-loop for i in-ref l by #'cdr)) ("loopy with function" (loopy (list-ref i l :by #'cdr))))))
Form x faster than next Total runtime # of GCs Total GC runtime loopy 1.00 0.017130 0 0 cl-loop with function 1.00 0.017137 0 0 loopy with function 1.01 0.017157 0 0 cl-loop slowest 0.017380 0 0 -
for VAR across ARRAY
:(let ((a (make-vector 1000 1))) (bench-multi :times 10000 :forms (("cl-loop" (cl-loop for i across a)) ("loopy" (loopy (array i a))))))
Form x faster than next Total runtime # of GCs Total GC runtime loopy 1.14 0.433241 0 0 cl-loop slowest 0.495099 0 0
-
for VAR across-ref ARRAY
:(let ((a (make-vector 1000 1))) (bench-multi :times 10000 :forms (("cl-loop" (cl-loop for i across-ref a)) ("loopy" (loopy (array-ref i a))))))
Form x faster than next Total runtime # of GCs Total GC runtime loopy 1.28 0.276715 0 0 cl-loop slowest 0.354138 0 0 -
for VAR being the elements of SEQUENCE
:(let ((a (make-vector 1000 1)) (l (make-list 1000 1))) (bench-multi :times 10000 :forms (("cl-loop array" (cl-loop for i being the elements of a)) ("loopy array" (loopy (seq i a))) ("cl-loop list" (cl-loop for i being the elements of l)) ("loopy list" (loopy (seq i l))))))
Form x faster than next Total runtime # of GCs Total GC runtime loopy list 1.00 0.639563 0 0 cl-loop list 1.25 0.640058 0 0 loopy array 1.04 0.797108 0 0 cl-loop array slowest 0.825234 0 0 -
for VAR being the elements of-ref SEQUENCE
:(let ((a (make-vector 1000 1)) (l (make-list 1000 1))) (bench-multi :times 10000 :forms (("cl-loop array" (cl-loop for i being the elements of-ref a)) ("loopy array" (loopy (seq-ref i a))) ("cl-loop list" (cl-loop for i being the elements of-ref l)) ("loopy list" (loopy (seq-ref i l))))))
Form x faster than next Total runtime # of GCs Total GC runtime cl-loop array 1.00 0.278885 0 0 loopy array 1.04 0.279238 0 0 loopy list 1.00 0.289703 0 0 cl-loop list slowest 0.290474 0 0 -
for VAR = EXPR1 then EXPR2
: Differences are due to the implementation ofrepeat
vscycle
.(bench-multi :times 10000 :forms (("cl-loop" (cl-loop for i = 0 then (1+ i) repeat 100)) ("loopy" (loopy (set i 0 (1+ i)) (cycle 100)))))
Form x faster than next Total runtime # of GCs Total GC runtime cl-loop 1.01 0.048465 0 0 loopy slowest 0.048762 0 0
The clauses always
, never
, thereis
, and iter-by
are currently not
implemented in Loopy, but their usages can be matched with existing
(though currently less convenient) loop commands.
-
repeat
:(bench-multi :times 1000 :forms (("cl-loop" (cl-loop repeat 100)) ; Uses `>=' and `1-'. ("loopy" (loopy (cycle 100))))) ; Uses `<' and `1+'.
Form x faster than next Total runtime # of GCs Total GC runtime loopy 1.04 0.002680 0 0 cl-loop slowest 0.002779 0 0 -
while
: Inloopy
, thewhile
command is short for(when COND (leave))
and is evaluated in place. Incl-loop
, thewhile
clause adds a condition to the internalwhile
loop. This is probably the cause of any difference in speed, though in practice it would only be a very small part of the loop’s execution time.(bench-multi :times 10000 :forms (("cl-loop v1" (cl-loop while t repeat 100)) ("loopy v1" (loopy (while t) (repeat 100))) ;; Try opposite order. ("cl-loop v2" (cl-loop repeat 100 while t)) ("loopy v2" (loopy (repeat 100) (while t)))))
Form x faster than next Total runtime # of GCs Total GC runtime loopy v1 1.00 0.027067 0 0 loopy v2 1.02 0.027115 0 0 cl-loop v1 1.03 0.027617 0 0 cl-loop v2 slowest 0.028419 0 0 -
until
: See the details ofwhile
above for a possible explanation of any differences.(bench-multi :times 10000 :forms (("cl-loop v1" (cl-loop until nil repeat 100)) ("loopy v1" (loopy (until nil) (repeat 100))) ;; Try opposite order. ("cl-loop v2" (cl-loop repeat 100 until nil)) ("loopy v2" (loopy (repeat 100) (until nil)))))
Form x faster than next Total runtime # of GCs Total GC runtime loopy v1 1.00 0.026887 0 0 loopy v2 1.02 0.027005 0 0 cl-loop v2 1.00 0.027451 0 0 cl-loop v1 slowest 0.027484 0 0
cl-loop
uses special considerations for append
, collect
, and nconc
using combinations append
, nconc
, push
, nreverse
, and reverrse
depending on how variables are being declared and used. loopy
is currently
much simpler, such as using push
and nreverse
for collect
when no
explicitly variable is given (as in (collect some-val)
) and using append
otherwise.
You should note that naming the variables into which you append
,
collect
, or nconc
can be much slower, since the macros can no longer use
tricks with reverse
and nreverse
. If they did, you might try to access
the accumulation variable before it was reversed. More control can be used
via the accum-opt
special macro argument.
-
collect
:(let ((l (number-sequence 1 1000))) (bench-multi :times 10000 :forms (("cl-loop" (cl-loop for i in l collect (* (1+ i) 2))) ("loopy" (loopy (list i l) (collect (* (1+ i) 2)))) ("mapcar" (mapcar (lambda (i) (* (1+ i) 2)) l)) ("cl-mapcar" (cl-mapcar (lambda (i) (* (1+ i) 2)) l)) ("dash" (-map (lambda (i) (* (1+ i) 2)) l)) ("anaphoric dash" (--map (* (1+ it) 2) l)) ("dolist" (let ((result)) (dolist (i l (nreverse result)) (push (* (1+ i) 2) result)))))))
Form x faster than next Total runtime # of GCs Total GC runtime dolist 1.02 0.651358 0 0 loopy 1.01 0.663928 0 0 cl-loop 1.44 0.667420 0 0 dash 1.00 0.963277 0 0 cl-mapcar 1.00 0.963896 0 0 mapcar 1.04 0.964183 0 0 anaphoric dash slowest 1.005722 0 0 -
append
andnconc
:(let ((l (number-sequence 1 1000))) (bench-multi :times 10000 :forms (("cl-loop append" (cl-loop for i in l append (list (* (1+ i) 2) (* (1+ i) 3)))) ("cl-loop nconc" (cl-loop for i in l nconc (list (* (1+ i) 2) (* (1+ i) 3)))) ("loopy append" (loopy (list i l) (append (list (* (1+ i) 2) (* (1+ i) 3))))) ("loopy nconc" (loopy (list i l) (nconc (list (* (1+ i) 2) (* (1+ i) 3))))) ("mapcan" (mapcan (lambda (i) (list (* (1+ i) 2) (* (1+ i) 3))) l)) ; C function. ("cl-mapcan" (cl-mapcan (lambda (i) (list (* (1+ i) 2) (* (1+ i) 3))) l)) ("dash" (-mapcat (lambda (i) (list (* (1+ i) 2) (* (1+ i) 3))) l)) ("anaphoric dash" (--mapcat (list (list (* (1+ it) 2) (* (1+ it) 3))) l)))))
Form x faster than next Total runtime # of GCs Total GC runtime cl-loop nconc 1.01 1.431169 0 0 loopy nconc 1.05 1.444169 0 0 mapcan 1.00 1.518999 0 0 cl-mapcan 1.16 1.521155 0 0 cl-loop append 1.10 1.768662 0 0 anaphoric dash 1.03 1.941612 0 0 loopy append 1.16 1.999173 0 0 dash slowest 2.320142 0 0
-
concat
: This difference might be becausecl-loop
usescl-callf
, whileloopy
currently usesconcat
andsetq
in all cases.seq-mapcat
andmapconcat
might be much faster because they only callconcat
once after mapping across all values. On the other hand,loopy
andcl-loop
currently callconcat
for each iteration. On the other hand,cl-loop
callsconcat
for each iteration, as doesloopy
if the accumulation variable is explicitly named.(let ((l (make-list 100 (make-string 100 ?a)))) (bench-multi :times 1000 :forms (("cl-loop" (cl-loop for i in l concat (capitalize i))) ;; Here, `loopy' calls `concat' once: ("loopy implicit" (loopy (list i l) (concat (capitalize i)))) ;; Here, `loopy', calls `concat' each iteration: ("loopy explicit" (loopy (list i l) (concat my-str (capitalize i)))) ("mapconcat" (mapconcat #'capitalize l "")) ; C function. ("concat" (apply #'concat (mapcar #'capitalize l))) ;; `cl-concatenate' is alias of `seq-concatenate'. ("cl-concatenate" (apply #'cl-concatenate 'string (mapcar #'capitalize l))) ("seq-macpcat" (seq-mapcat #'capitalize l 'string)))))
Form x faster than next Total runtime # of GCs Total GC runtime concat 1.01 0.228874 0 0 cl-concatenate 1.00 0.231995 0 0 seq-macpcat 1.03 0.232622 0 0 mapconcat 1.02 0.238497 0 0 loopy implicit 3.17 0.244310 0 0 loopy explicit 1.01 0.774461 0 0 cl-loop slowest 0.783207 0 0 -
vconcat
: As withconcat
above,cl-loop
callsconcat
for each iteration, while other functions create a list of sequences and then callconcat
once.(let* ((l (make-list 100 (number-sequence 1 1000)))) (bench-multi :times 1000 :forms (("cl-loop" (cl-loop for i in l vconcat (cdr i))) ("loopy implicit" (loopy (list i l) (vconcat (cdr i)))) ("loopy explicit" (loopy (list i l) (vconcat my-vect (cdr i)))) ("vconcat" (apply #'vconcat (mapcar #'cdr l))) ("cl-concatenate" (apply #'cl-concatenate 'vector (mapcar #'cdr l))) ;; This concatenates once after the `cdr' of each list is gotten. ("seq-macpcat" (seq-mapcat #'cdr l 'vector)))))
Form x faster than next Total runtime # of GCs Total GC runtime seq-macpcat 1.00 0.559997 0 0 cl-concatenate 1.00 0.560870 0 0 loopy implicit 1.01 0.561155 0 0 vconcat 52.76 0.564348 0 0 cl-loop 1.00 29.774690 37 5.212632 loopy explicit slowest 29.788736 37 5.213497 -
count
: Not sure why-count
is so much faster than the others.(let* ((l (loopy (cycle 1000) (collect (cl-evenp (random 2)))))) (bench-multi :times 10000 :forms (("cl-loop" (cl-loop for i in l count i)) ("loopy" (loopy (list i l) (count i))) ("cl-count" (cl-count t l)) ("cl-count-if" (cl-count-if #'identity l)) ("seq-count" (seq-count #'identity l)) ("-count" (-count #'identity l)))))
Form x faster than next Total runtime # of GCs Total GC runtime -count 1.55 0.241444 0 0 cl-count 1.01 0.373984 0 0 loopy 1.02 0.378541 0 0 cl-loop 1.94 0.385982 0 0 cl-count-if 1.07 0.750654 0 0 seq-count slowest 0.799656 0 0 -
sum
:(let* ((l (number-sequence 1 1000))) (bench-multi :times 10000 :forms (("cl-loop" (cl-loop for i in l sum (1+ i))) ("loopy" (loopy (list i l) (sum (1+ i)))) ("mapcar and apply" (apply #'+ (mapcar #'1+ l))) ("mapcar and cl-reduce" (cl-reduce #'+ (mapcar #'1+ l))) ("mapcar and seq-reduce" (seq-reduce #'+ (mapcar #'1+ l) 0)) ("mapcar and -reduce" (-reduce #'+ (mapcar #'1+ l))))))
Form x faster than next Total runtime # of GCs Total GC runtime mapcar and apply 1.13 0.426344 0 0 loopy 1.01 0.483621 0 0 cl-loop 1.33 0.487986 0 0 mapcar and -reduce 1.79 0.647770 0 0 mapcar and cl-reduce 1.04 1.157679 0 0 mapcar and seq-reduce slowest 1.198302 0 0 -
maximize
:(let* ((l (number-sequence 1 1000))) (bench-multi :times 10000 :forms (("cl-loop" (cl-loop for i in l maximize (1- i))) ("loopy" (loopy (list i l) (max (1- i)))) ("mapcar and apply" (apply #'max (mapcar #'1- l))) ("mapcar and cl-reduce" (cl-reduce #'max (mapcar #'1- l))) ("mapcar and seq-reduce" (seq-reduce #'max (mapcar #'1- l) -1.0e+INF)) ("mapcar and -reduce" (-reduce #'max (mapcar #'1- l))))))
Form x faster than next Total runtime # of GCs Total GC runtime mapcar and apply 1.07 0.499009 0 0 loopy 1.34 0.535440 0 0 mapcar and -reduce 1.18 0.718134 0 0 cl-loop 1.46 0.848273 0 0 mapcar and cl-reduce 1.02 1.239439 0 0 mapcar and seq-reduce slowest 1.261978 0 0 -
minimize
:(let* ((l (number-sequence 1 1000))) (bench-multi :times 10000 :forms (("cl-loop" (cl-loop for i in l minimize (1- i))) ("loopy" (loopy (list i l) (min (1- i)))) ("mapcar and apply" (apply #'min (mapcar #'1- l))) ("mapcar and cl-reduce" (cl-reduce #'min (mapcar #'1- l))) ("mapcar and seq-reduce" (seq-reduce #'min (mapcar #'1- l) +1.0e+INF)) ("mapcar and -reduce" (-reduce #'min (mapcar #'1- l))))))
Form x faster than next Total runtime # of GCs Total GC runtime mapcar and apply 1.06 0.501541 0 0 loopy 1.39 0.533328 0 0 mapcar and -reduce 1.15 0.741511 0 0 cl-loop 1.49 0.850181 0 0 mapcar and cl-reduce 1.02 1.262570 0 0 mapcar and seq-reduce slowest 1.282561 0 0
(let ((alist (loopy (numbers n 0 1000)
(collect (cons n (+ n 2000))))))
(bench-multi
:times 10000
:forms (("cl-loop" (cl-loop for (i . j) in alist collect (+ i j)))
("loopy" (loopy (list (i . j) alist) (collect (+ i j))))
("pcase-lambda" (mapcar (pcase-lambda (`(,i . ,j)) (+ i j)) alist))
("cl-function" (mapcar (cl-function (lambda ((i . j)) (+ i j))) alist))
("-lambda" (mapcar (-lambda ((i . j)) (+ i j)) alist)))))
Form | x faster than next | Total runtime | # of GCs | Total GC runtime |
---|---|---|---|---|
cl-loop | 1.01 | 0.814436 | 0 | 0 |
loopy | 2.31 | 0.821378 | 0 | 0 |
-lambda | 1.10 | 1.900049 | 0 | 0 |
pcase-lambda | 1.01 | 2.082832 | 0 | 0 |
cl-function | slowest | 2.101464 | 0 | 0 |