Use plain array instead of set for tracking watched atoms

Turns out to be much, much faster in most cases
This commit is contained in:
Dan Holmsand 2015-09-27 16:04:06 +02:00
parent 7cd2d7d860
commit e39bb33ccc
1 changed files with 40 additions and 23 deletions

View File

@ -6,15 +6,16 @@
(declare ^:dynamic *ratom-context*) (declare ^:dynamic *ratom-context*)
(defonce cached-reactions {}) (defonce cached-reactions {})
(defonce ^boolean debug false)
(defonce ^boolean silent false)
(defonce generation 0)
(defonce -running (clojure.core/atom 0))
(defn ^boolean reactive? [] (defn ^boolean reactive? []
(not (nil? *ratom-context*))) (not (nil? *ratom-context*)))
(defonce ^boolean debug false)
(defonce ^boolean silent false)
(defonce generation 0)
(defonce -running (clojure.core/atom 0)) ;;; Utilities
(defn running [] (defn running []
(+ @-running (+ @-running
@ -39,20 +40,28 @@
(defn- notify-deref-watcher! [derefable] (defn- notify-deref-watcher! [derefable]
(when-some [obj *ratom-context*] (when-some [obj *ratom-context*]
(let [captured (.-cljsCaptured obj)] (let [c (.-cljsCaptured obj)]
(set! (.-cljsCaptured obj) (if (nil? c)
(if (nil? captured) (set! (.-cljsCaptured obj) (array derefable))
(let [old (.-watching obj)] (when (== -1 (.indexOf c derefable))
(if (and (== 1 (count old)) (.push c derefable)))))
(identical? derefable (first old))) nil)
;; Optimize common case of one deref
old (defn- ^number arr-len [x]
#{derefable})) (if (nil? x) 0 (alength x)))
(conj captured derefable))))))
(defn- ^boolean arr-eq [x y]
(let [len (arr-len x)]
(and (== len (arr-len y))
(loop [i 0]
(or (== i len)
(if (identical? (aget x i) (aget y i))
(recur (inc i))
false))))))
(def reaction-counter 0) (def reaction-counter 0)
(defn reaction-key [r] (defn- reaction-key [r]
(if-some [k (.-reaction-id r)] (if-some [k (.-reaction-id r)]
k k
(->> reaction-counter inc (->> reaction-counter inc
@ -84,6 +93,7 @@
(pr-writer (binding [*ratom-context* nil] (-deref a)) writer opts) (pr-writer (binding [*ratom-context* nil] (-deref a)) writer opts)
(-write writer ">")) (-write writer ">"))
;;; Atom ;;; Atom
(defprotocol IReactiveAtom) (defprotocol IReactiveAtom)
@ -340,12 +350,17 @@
(_check-clean [this] (_check-clean [this]
(when (== dirtyness maybe-dirty) (when (== dirtyness maybe-dirty)
(let [ar auto-run] (let [ar auto-run
len (arr-len watching)]
(set! auto-run nil) (set! auto-run nil)
(doseq [w watching :while (== dirtyness maybe-dirty)] (loop [i 0]
(when (and (instance? Reaction w) (when (< i len)
(false? (._check-clean w))) (let [w (aget watching i)]
(._try-run w this))) (when (and (instance? Reaction w)
(false? (._check-clean w)))
(._try-run w this)))
(when (== dirtyness maybe-dirty)
(recur (inc i)))))
(set! auto-run ar)) (set! auto-run ar))
(when (== dirtyness maybe-dirty) (when (== dirtyness maybe-dirty)
(set! dirtyness clean))) (set! dirtyness clean)))
@ -369,10 +384,12 @@
(_update-watching [this derefed] (_update-watching [this derefed]
(doseq [w derefed] (doseq [w derefed]
(when-not (contains? watching w) (when (or (nil? watching)
(== -1 (.indexOf watching w)))
(-add-watch w this handle-reaction-change))) (-add-watch w this handle-reaction-change)))
(doseq [w watching] (doseq [w watching]
(when-not (contains? derefed w) (when (or (nil? derefed)
(== -1 (.indexOf derefed w)))
(-remove-watch w this))) (-remove-watch w this)))
(set! watching derefed) (set! watching derefed)
nil) nil)
@ -394,7 +411,7 @@
(let [oldstate state (let [oldstate state
res (capture-derefed f this) res (capture-derefed f this)
derefed (-captured this)] derefed (-captured this)]
(when (not= derefed watching) (when-not (arr-eq derefed watching)
(._update-watching this derefed)) (._update-watching this derefed))
(set! dirtyness clean) (set! dirtyness clean)
(when-not nocache? (when-not nocache?