]> git.eshelyaron.com Git - emacs.git/commitdiff
Further seq-uniq speed-ups for lists
authorLars Ingebrigtsen <larsi@gnus.org>
Fri, 12 Aug 2022 13:15:11 +0000 (15:15 +0200)
committerLars Ingebrigtsen <larsi@gnus.org>
Fri, 12 Aug 2022 13:16:39 +0000 (15:16 +0200)
* lisp/emacs-lisp/seq.el (seq-uniq): Speed up more for long lists
(bug#57079).

lisp/emacs-lisp/seq.el
test/lisp/emacs-lisp/seq-tests.el

index 6ddd8de6e8d2a49b008328ca4968196bde0ba8f0..b6f0f66e5b1d657fdc51c5521e7759c7962a49e2 100644 (file)
@@ -458,11 +458,21 @@ TESTFN is used to compare elements, or `equal' if TESTFN is nil."
 (cl-defmethod seq-uniq ((sequence list) &optional testfn)
   (let ((result nil))
     (if (not testfn)
-        ;; Fast path.
-        (while sequence
-          (unless (member (car sequence) result)
-            (push (car sequence) result))
-          (pop sequence))
+        ;; Fast path.  If the list is long, use a hash table to speed
+        ;; things up even more.
+        (let ((l (length sequence)))
+          (if (> l 100)
+              (let ((hash (make-hash-table :test #'equal :size l)))
+                (while sequence
+                  (unless (gethash (car sequence) hash)
+                    (setf (gethash (car sequence) hash) t)
+                    (push (car sequence) result))
+                  (setq sequence (cdr sequence))))
+            ;; Short list.
+            (while sequence
+              (unless (member (car sequence) result)
+                (push (car sequence) result))
+              (pop sequence))))
       ;; Slower path.
       (while sequence
         (unless (seq-find (lambda (elem)
index a655377e6ccb72a6bd820e26c2c70070c6c4762b..1a27467d292f761710dfa9273e299c6825d56490 100644 (file)
@@ -570,7 +570,12 @@ Evaluate BODY for each created sequence.
                     (substring "2")
                     (substring "1"))))
     (should (equal (seq-uniq list) '("1" "2" "3")))
-    (should (equal (seq-uniq list #'eq) '("1" "2" "3" "2" "1")))))
+    (should (equal (seq-uniq list #'eq) '("1" "2" "3" "2" "1"))))
+  ;; Long lists have a different code path.
+  (let ((list (seq-map-indexed (lambda (_ i) i)
+                              (make-list 10000 nil))))
+    (should (= (length list) 10000))
+    (should (= (length (seq-uniq (append list list))) 10000))))
 
 (provide 'seq-tests)
 ;;; seq-tests.el ends here