Improve before-index performance #223 (#291)

Adding new protocol for performing the insert-before-idx operation, with
implementations for core collection types

Adding new functional test to confirm behavior when operating on a string

Adding benchmarks to compare new performance vs old implementation vs
core Clojure in a couple of cases
This commit is contained in:
Jeff Evans 2020-09-17 14:15:42 -05:00 committed by GitHub
parent 789881b3ad
commit 2e002c1270
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 60 additions and 6 deletions

View file

@ -335,3 +335,20 @@
(transform (walker number?) inc data)
(transform (walker-old number?) inc data)
))
(let [size 1000
middle-idx (/ size 2)
v -1
rng (range size)
data-vec (vec rng)
data-lst (apply list rng)]
(run-benchmark "before-index vs. srange in middle (vector)"
(setval (before-index middle-idx) v data-vec)
(setval (srange middle-idx middle-idx) [v] data-vec))
(run-benchmark "before-index vs. srange in middle (list)"
(setval (before-index middle-idx) v data-lst)
(setval (srange middle-idx middle-idx) [v] data-lst))
(run-benchmark "before-index at 0 vs. srange vs. cons (list)"
(setval (before-index 0) v data-lst)
(setval (srange 0 0) [v] data-lst)
(cons v data-lst)))

View file

@ -1010,11 +1010,10 @@
NONE)
(transform* [this vals structure next-fn]
(let [v (next-fn vals NONE)]
(if (identical? NONE v)
structure
;; TODO: make a more efficient impl
(setval (srange index index) [v] structure)
))))
(if
(identical? NONE v)
structure
(n/insert-before-idx structure index v)))))
(defrichnav
^{:doc "Navigates to the index of the sequence if within 0 and size. Transforms move element

View file

@ -474,6 +474,9 @@
(defprotocol FastEmpty
(fast-empty? [s]))
(defprotocol InsertBeforeIndex
(insert-before-idx [aseq idx val]))
(defnav PosNavigator [getter updater]
(select* [this structure next-fn]
(if-not (fast-empty? structure)
@ -691,3 +694,34 @@
((:end-fn end-fn) structure start)
(end-fn structure)
))
(defn- insert-before-index-list [lst idx val]
;; an implementation that is most efficient for list style structures
(let [[front back] (split-at idx lst)]
(concat front (cons val back))))
(extend-protocol InsertBeforeIndex
nil
(insert-before-idx [_ idx val]
(cond (= 0 idx) [val]
:else (i/throw-illegal "For a nil structure, can only insert before index 0, not at - " idx)))
#?(:clj java.lang.String :cljs string)
(insert-before-idx [aseq idx val]
(apply str (insert-before-index-list aseq idx val)))
#?(:clj clojure.lang.LazySeq :cljs cljs.core/LazySeq)
(insert-before-idx [aseq idx val]
(insert-before-index-list aseq idx val))
#?(:clj clojure.lang.IPersistentVector :cljs cljs.core/PersistentVector)
(insert-before-idx [aseq idx val]
(let [front (subvec aseq 0 idx)
back (subvec aseq idx)]
(into (conj front val) back)))
#?(:clj clojure.lang.IPersistentList :cljs cljs.core/List)
(insert-before-idx [aseq idx val]
(cond (= idx 0)
(cons val aseq)
:else (insert-before-index-list aseq idx val))))

View file

@ -1609,14 +1609,18 @@
(deftest before-index-test
(let [data [1 2 3]
datal '(1 2 3)]
datal '(1 2 3)
data-str "abcdef"]
(is (predand= vector? [:a 1 2 3] (setval (s/before-index 0) :a data)))
(is (predand= vector? [1 2 3] (setval (s/before-index 1) s/NONE data)))
(is (predand= vector? [1 :a 2 3] (setval (s/before-index 1) :a data)))
(is (predand= vector? [1 2 3 :a] (setval (s/before-index 3) :a data)))
; ensure inserting at index 0 in nil structure works, as in previous impl
(is (predand= vector? '[:a] (setval (s/before-index 0) :a nil)))
(is (predand= list? '(:a 1 2 3) (setval (s/before-index 0) :a datal)))
(is (predand= list? '(1 :a 2 3) (setval (s/before-index 1) :a datal)))
(is (predand= list? '(1 2 3 :a) (setval (s/before-index 3) :a datal)))
(is (predand= string? "abcxdef" (setval (s/before-index 3) (char \x) data-str)))
))
(deftest index-nav-test