-
-
Save purcell/34824f1b676e6188540cdf71c7cc9fc4 to your computer and use it in GitHub Desktop.
| (defun key-quiz--shuffle-list (list) | |
| "Shuffles LIST randomly, modying it in-place." | |
| (dolist (i (reverse (number-sequence 1 (1- (length list))))) | |
| (let ((j (random (1+ i))) | |
| (tmp (elt list i))) | |
| (setf (elt list i) (elt list j)) | |
| (setf (elt list j) tmp))) | |
| list) | |
| (defun key-quiz--shuffle-list-nreverse (list) | |
| "Shuffles LIST randomly, modying it in-place." | |
| (dolist (i (nreverse (number-sequence 1 (1- (length list))))) | |
| (let ((j (random (1+ i))) | |
| (tmp (elt list i))) | |
| (setf (elt list i) (elt list j)) | |
| (setf (elt list j) tmp))) | |
| list) | |
| (defun elfeed--shuffle (seq) | |
| "Destructively shuffle SEQ." | |
| (let ((n (length seq))) | |
| (prog1 seq ; don't use dotimes result (bug#16206) | |
| (dotimes (i n) | |
| (cl-rotatef (elt seq i) (elt seq (+ i (random (- n i))))))))) | |
| (defun faster-seq-sort-by (function pred sequence) | |
| "Sort SEQUENCE using PRED as a comparison function. | |
| Elements of SEQUENCE are transformed by FUNCTION before being | |
| sorted. FUNCTION must be a function of one argument." | |
| ;; This version is modified to avoid calling "random" twice every time the predicate is called. | |
| (seq-map 'cdr | |
| (sort (seq-map (lambda (x) (cons (funcall function x) x)) sequence) | |
| (lambda (a b) | |
| (funcall pred (car a) (car b)))))) | |
| (defun seq-sort-by--shuffle (seq) | |
| (seq-sort-by (lambda (_) (random)) #'<= seq)) | |
| (defun faster-seq-sort-by--shuffle (seq) | |
| (faster-seq-sort-by (lambda (_) (random)) #'<= seq)) | |
| (defun faster-seq-sort-by--shuffle (seq) | |
| (faster-seq-sort-by (lambda (_) (random)) #'<= seq)) | |
| (defun my-seq-shuffle (sequence) | |
| "Unrolled version of seq-sort-by" | |
| (seq-map 'cdr | |
| (sort (seq-map (lambda (x) (cons (random) x)) sequence) | |
| (lambda (a b) | |
| (<= (car a) (car b)))))) | |
| (defun hugot-shuffle (list) | |
| "Method from hugot, tweaked to avoid converting vecs back into lists when not needed" | |
| (let* ((vec (seq-into list 'vector)) | |
| (length (length vec)) | |
| (n 0)) | |
| (while (< n length) | |
| (let ((i (+ n (random (- length n)))) | |
| (tmp (aref vec n))) | |
| (setf (aref vec n) (aref vec i) | |
| (aref vec i) tmp)) | |
| (cl-incf n)) | |
| (if (vectorp list) | |
| vec | |
| (seq-into vec 'list)))) | |
| (defun elfeed-on-vec-shuffle (list) | |
| "Elfeed's method, but converting to vec" | |
| (let* ((seq (seq-into list 'vector))) | |
| (let ((n (length seq))) | |
| (prog1 (if (vectorp list) seq (seq-into seq 'list)) | |
| (dotimes (i n) | |
| (cl-rotatef (elt seq i) (elt seq (+ i (random (- n i)))))))))) | |
| (dolist (sorter '(key-quiz--shuffle-list | |
| key-quiz--shuffle-list-nreverse | |
| elfeed--shuffle | |
| seq-sort-by--shuffle | |
| faster-seq-sort-by--shuffle | |
| my-seq-shuffle | |
| hugot-shuffle | |
| elfeed-on-vec-shuffle | |
| )) | |
| (message "\n*** %s ***" sorter) | |
| (dolist (list-type '(list vector)) | |
| (let ((big-list (seq-into (number-sequence 1 5000) list-type)) | |
| (reps 100) | |
| ;;(gc-cons-threshold (* 128 1024 1024)) | |
| ) | |
| (message "-- %s:" list-type) | |
| (garbage-collect) | |
| (benchmark reps `(funcall ',sorter big-list)) | |
| ))) |
| *** key-quiz--shuffle-list *** | |
| -- list: | |
| Elapsed time: 6.650204s | |
| -- vector: | |
| Elapsed time: 0.240027s | |
| *** key-quiz--shuffle-list-nreverse *** | |
| -- list: | |
| Elapsed time: 6.742569s | |
| -- vector: | |
| Elapsed time: 0.244173s | |
| *** elfeed--shuffle *** | |
| -- list: | |
| Elapsed time: 11.157770s | |
| -- vector: | |
| Elapsed time: 0.236123s | |
| *** seq-sort-by--shuffle *** | |
| -- list: | |
| Elapsed time: 0.644674s | |
| -- vector: | |
| Elapsed time: 0.685449s | |
| *** faster-seq-sort-by--shuffle *** | |
| -- list: | |
| Elapsed time: 0.573194s | |
| -- vector: | |
| Elapsed time: 0.572976s | |
| *** my-seq-shuffle *** | |
| -- list: | |
| Elapsed time: 0.470165s | |
| -- vector: | |
| Elapsed time: 0.465306s | |
| *** hugot-shuffle *** | |
| -- list: | |
| Elapsed time: 0.209031s | |
| -- vector: | |
| Elapsed time: 0.197855s | |
| *** elfeed-on-vec-shuffle *** | |
| -- list: | |
| Elapsed time: 0.252475s | |
| -- vector: | |
| Elapsed time: 0.239680s |
@hugot Thanks. How many elements were in those sequences you benchmarked the sorting of?
@alphapapa Same as in the original gist, 5000. Although I now have my questions about the reliability of that. I didn't notice it initially, but using seq-take on the obarray seems like an overly creative way to create a big list when we have number-sequence. Not to mention, what if there's less than 5000 symbols in the obarray? 🙃 Would probably best to change that bit of code if we were to use this as a reliable benchmark.
Yes, I'd recommend making a specific sequence, and testing with larger sizes, too.
@hugot your method is faster still if you avoid converting vecs back into lists when the input was a list.
I updated the gist with these variations, and a better source of initial input.
@purcell great, thanks!
Hey! I needed a shuffle function too and came up with this one. It cheats because it converts whatever sequence it got into a vector and then converts that to a list in the end, but it's quite fast :)