Add iterate, copy and remove operations
authorMatthew Mondor <mmondor@pulsar-zone.net>
Sun, 13 Jul 2014 17:12:17 +0000 (17:12 +0000)
committerMatthew Mondor <mmondor@pulsar-zone.net>
Sun, 13 Jul 2014 17:12:17 +0000 (17:12 +0000)
mmsoftware/cl/test/string-dict-perf-test.lisp

index 9eca8e2..f6ff24c 100644 (file)
@@ -1,9 +1,10 @@
-;;; $Id: string-dict-perf-test.lisp,v 1.2 2012/09/24 06:44:24 mmondor Exp $
+;;; $Id: string-dict-perf-test.lisp,v 1.3 2014/07/13 17:12:17 mmondor Exp $
 
 ;;; Verify the performance of various sized hash tables, alists, plists
 ;;; and vectors for key-value storage.  Also test versus DO-SXHASH-CASE.
 ;;; Also verify the time taken to populate the content, not only look it
 ;;; up.  Use random words from a dictionary file for both key and content.
+;;; Does not test sorted set variants.
 
 (declaim (optimize (speed 3) (safety 0) (debug 0)))
 
                     key)))
     (getf plist the-key)))
 
+(defun plist-iterate (plist)
+  (loop
+     for (key val) on plist by #'cddr)
+  nil)
+
+(defun plist-copy (plist)
+  (copy-list plist))
+
+(defun plist-remove (plist key)
+  (let ((the-key (if (stringp key)
+                    (intern (string-upcase key) :keyword)
+                    key)))
+    (remf plist the-key)
+    plist))
+
 
 ;;; Old-style AList
 
        (cdr pair)
        nil)))
 
+(defun alist-iterate (alist)
+  (loop
+     for (key . val) in alist)
+  nil)
+
+(defun alist-copy (alist)
+  (copy-alist alist))
+
+(defun alist-remove (alist key)
+  (delete (assoc key alist :test #'string-equal) alist))
+
 
 ;;; Vector used instead of a list, holding PAIR objects.
 
   value)
 
 (defun vector-populate (data)
+  (declare (optimize (speed 3) (safety 0) (debug 0)))
   (loop
      with length = (length (data-vector data))
-     with vector = (make-array length)
+     with vector = (make-array length :fill-pointer length)
      with alist = (data-alist data)
      for (key . val) in alist
      for i of-type fixnum from 0
        (return-from vector-lookup (pair-value pair)))
   nil)
 
+(defun vector-iterate (vector)
+  (loop
+     for pair across vector)
+  nil)
 
-;;; Famous hash-table, generally used a lot for this kind of problem.
+(defun vector-copy (vector)
+  (declare (optimize (speed 3) (safety 0) (debug 0)))
+  (let ((length (length vector)))
+    (declare (type fixnum length))
+    (make-array length
+               :fill-pointer length
+               :initial-contents vector)))
+
+(defun vector-remove (vector key)
+  (declare (optimize (speed 3) (safety 0) (debug 0)))
+  (let* ((length (length vector))
+        (last (1- length)))
+    (declare (type fixnum length last))
+    (when (zerop length)
+      (return-from vector-remove vector))
+    (let ((i (loop
+               for i of-type fixnum from 0
+               for pair across vector
+               for match = (string-equal (pair-key pair) key)
+               until match
+               finally (return (if match i -1)))))
+      (declare (type fixnum i))
+      (when (= -1 i)
+       (return-from vector-remove vector))
+      ;; Match, swap last entry with matched index and decrease fill-pointer
+      (unless (= last i)
+       (psetf (aref vector last) (aref vector i)
+              (aref vector i) (aref vector last)))
+      (setf (fill-pointer vector) last)
+      vector)))
+
+
+;;; Hash-table, generally used for this kind of problem.
 
 (defun hashtable-populate (data)
   (loop
 (defun hashtable-lookup (table key)
   (gethash key table))
 
+(defun hashtable-iterate (table)
+  (loop
+     for key being each hash-key of table using (hash-value value))
+  nil)
+
+(defun hashtable-copy (table)
+  (declare (optimize (speed 3) (safety 0) (debug 0)))
+  (loop
+     with newtable = (make-hash-table :test #'equalp)
+     for key being each hash-key of table using (hash-value value)
+     do
+       (setf (gethash key newtable) value)
+     finally (return newtable)))
+
+(defun hashtable-remove (table key)
+  (remhash key table)
+  table)
+
+
+#|
 
 ;;; Special system which creates a compiled-function of inline unrolled
 ;;; code instead of iteration.  Very slow population, faster than usual
 ;;; iteration but still O(n).  Uses SXHASH to compare FIXNUMs instead of
-;;; strings.
+;;; strings.  Errors in case a collision happens at compile-time.
 
 (defmacro do-sxhash-case ((var) match-code-pairs)
   (let ((var-hash (gensym)))
 (defun sxhashcase-lookup (function key)
   (funcall function key))
 
+;;; Just call the lookup function with an unlikely to match string
+(defun sxhashcase-iterate (function)
+  (funcall function "1357198437518"))
+
+;;; Irrelevant, would require recompilation
+(defun sxhashcase-copy (function)
+  function)
+
+;;; Irrelevant, would require recompilation
+(defun sxhashcase-remove (function)
+  function)
+
+|#
+
 
 ;;; Similar to the above in that this system uses SXHASH instead of string
 ;;; comparisons, but O(n) iteration using an AList of fixnum/value pairs.
+;;; XXX Should error if collision
 
 (defun sxhashalist-populate (data)
   (loop
        (cdr pair)
        nil)))
 
+(defun sxhashalist-iterate (alist)
+  (loop
+     for (key . val) in alist)
+  nil)
+
+(defun sxhashalist-copy (alist)
+  (copy-alist alist))
+
+(defun sxhashalist-remove (alist key)
+  (delete (assoc (sxhash (string-downcase key)) alist :test #'=) alist))
+
 
 ;;; Similar to the above in that this system uses SXHASH instead of string
 ;;; comparisons, but O(n) iteration using a PList of fixnum/value pairs.
+;;; XXX Should error if collision
 
 (defun sxhashplist-populate (data)
   (loop
 (defun sxhashplist-lookup (plist key)
   (getf plist (sxhash (string-downcase key))))
 
+(defun sxhashplist-iterate (plist)
+  (loop
+     for (key val) on plist by #'cddr)
+  nil)
+
+(defun sxhashplist-copy (plist)
+  (copy-list plist))
+
+(defun sxhashplist-remove (plist key)
+  (remf plist (sxhash (string-downcase key)))
+  plist)
+
 
 ;;; Permits to calculate elapsed ticks when executing BODY.
 ;;; The number of elapsed ticks is in *REAL-TICKS* afterwards.
 
 ;;; Directory of above systems to test
 
-(defstruct (system (:constructor make-system (name populate lookup)))
-  name populate lookup)
+(defstruct (system (:constructor make-system
+                                (name populate lookup iterate copy remove)))
+  name populate lookup iterate copy remove)
 
 (defparameter *systems*
-  (list (make-system "plist" #'plist-populate #'plist-lookup)
-       (make-system "alist" #'alist-populate #'alist-lookup)
-       (make-system "vector" #'vector-populate #'vector-lookup)
-       (make-system "hash-table" #'hashtable-populate #'hashtable-lookup)
+  (list (make-system "plist" #'plist-populate #'plist-lookup #'plist-iterate
+                    #'plist-copy #'plist-remove)
+       (make-system "alist" #'alist-populate #'alist-lookup #'alist-iterate
+                    #'alist-copy #'alist-remove)
+       (make-system "vector" #'vector-populate #'vector-lookup
+                    #'vector-iterate #'vector-copy #'vector-remove)
+       (make-system "hash-table" #'hashtable-populate #'hashtable-lookup
+                    #'hashtable-iterate #'hashtable-copy #'hashtable-remove)
 ;      XXX Way too slow to populate COUNT times, also "cheating" compared
 ;      to iteration as it results in inline unrolled comparison code.
 ;      (make-system "sxhash-case" #'sxhashcase-populate
-;                   #'sxhashcase-lookup)
+;                   #'sxhashcase-lookup #'sxhashcase-iterate
+;                   #'sxhashcase-copy #'sxhashcase-remove)
        (make-system "sxhash-alist" #'sxhashalist-populate
-                    #'sxhashalist-lookup)
+                    #'sxhashalist-lookup #'sxhashalist-iterate
+                    #'sxhashalist-copy #'sxhashalist-remove)
        (make-system "sxhash-plist" #'sxhashplist-populate
-                    #'sxhashplist-lookup)))
+                    #'sxhashplist-lookup #'sxhashplist-iterate
+                    #'sxhashplist-copy #'sxhashplist-remove)))
 
 
 ;;; Test every system above, iterating COUNT times for every subtest and
   (declare (type fixnum size count))
   (with-accessors ((name system-name)
                   (populate system-populate)
-                  (lookup system-lookup)) system
+                  (lookup system-lookup)
+                  (iterate system-iterate)
+                  (copy system-copy)
+                  (remove system-remove)) system
     (let* ((data (word-pairs size))
           (dict (with-real-ticks
                     (loop
                        repeat count
                        for dict = (funcall populate data)
                        finally (return dict))))
-          (populate-ticks *real-ticks*))
-      (with-real-ticks
-         (loop
-            with vector = (data-vector data)
-            repeat count
-            for key = (svref vector (random size))
-            do
-              (funcall lookup dict key)))
-      (let ((lookup-match-ticks *real-ticks*))
+          (populate-ticks *real-ticks*)
+          (iterate-ticks (progn
+                           (with-real-ticks
+                               (loop
+                                  repeat count
+                                  do
+                                    (funcall iterate dict)))
+                           *real-ticks*))
+          lookup-match-ticks
+          lookup-miss-ticks
+          copy-ticks
+          remove-ticks)
+      (progn
+       (with-real-ticks
+           (loop
+              with vector = (data-vector data)
+              repeat count
+              for key = (svref vector (random size))
+              do
+                (funcall lookup dict key)))
+       (setf lookup-match-ticks *real-ticks*))
+      (progn
        (with-real-ticks
            (loop
               repeat count
               do
                 (funcall lookup dict "worijworjgowirjgw")))
-       (let ((lookup-miss-ticks *real-ticks*))
-         (format t
-  "~&Type: ~A, Size: ~D, Populate: ~D, Lookup-match: ~D, Lookup-miss: ~D~%"
-                 name size
-                 populate-ticks lookup-match-ticks lookup-miss-ticks))))))
+       (setf lookup-miss-ticks *real-ticks*))
+      (let ((copies
+            (prog1
+                (with-real-ticks
+                    (loop
+                       repeat count
+                       collect (funcall copy dict)))
+              (setf copy-ticks *real-ticks*))))
+       (with-real-ticks
+           (loop
+              with vector = (data-vector data)
+              repeat count
+              for key = (svref vector (random size))
+              for dict in copies
+              do
+                (setf dict (funcall remove dict key))))
+               (setf remove-ticks *real-ticks*))
+      (format t
+             "~&Type: ~A, Size: ~D, Populate: ~D, Lookup-match: ~D, ~
+             Lookup-miss: ~D, Iterate: ~D, Copy: ~D, Remove: ~D~%"
+             name size
+             populate-ticks lookup-match-ticks lookup-miss-ticks
+             iterate-ticks copy-ticks remove-ticks))))