10x perf improvement on safe-merge

safe merge was using way too much inefficient code for such an important
function
it is rewritten using a reduce. the performance improvement is 10 times
and should really show up when adding messages
in repl session the new merge was much slower on the error case of merging
fx with common keys but it must never happen in production as it means
the app is broken

status-im.utils.fx> (time (dotimes [x 100] (fast-merge {:a 1 :b 2 :filters/load-filters [{:a 1 :b 2}]} {:c 3 :filters/load-filters [{:d 1 :b x}]})))
"Elapsed time: 19.000000 msecs"
nil
status-im.utils.fx> (time (dotimes [x 100] (safe-merge {:a 1 :b 2 :filters/load-filters [{:a 1 :b 2}]} {:c 3 :filters/load-filters [{:d 1 :b x}]})))
"Elapsed time: 183.000000 msecs"

status-im.utils.fx> (time (dotimes [x 100] (fast-merge {:a 1 :c 2 :filters/load-filters [{:a 1 :b 2}]} {:c 3 :filters/load-filters [{:d 1 :b x}]})))
"Elapsed time: 2224.000000 msecs"
This commit is contained in:
yenda 2019-09-15 13:57:13 +02:00
parent 747dec908e
commit ee042bd1c4
No known key found for this signature in database
GPG Key ID: 0095623C0069DCE6
1 changed files with 20 additions and 19 deletions

View File

@ -4,14 +4,14 @@
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[taoensso.timbre :as log] [taoensso.timbre :as log]
status-im.utils.handlers) status-im.utils.handlers)
(:refer-clojure :exclude [merge])) (:refer-clojure :exclude [merge reduce]))
(defn- update-db [cofx fx] (defn- update-db [cofx fx]
(if-let [db (:db fx)] (if-let [db (:db fx)]
(assoc cofx :db db) (assoc cofx :db db)
cofx)) cofx))
(def ^:private mergable-keys (def ^:private mergeable-keys
#{:chat-received-message/add-fx #{:chat-received-message/add-fx
:filters/load-filters :filters/load-filters
:pairing/set-installation-metadata :pairing/set-installation-metadata
@ -27,16 +27,17 @@
(defn- safe-merge [fx new-fx] (defn- safe-merge [fx new-fx]
(if (:merging-fx-with-common-keys fx) (if (:merging-fx-with-common-keys fx)
fx fx
(let [common-keys (set/intersection (into #{} (keys fx)) (clojure.core/reduce (fn [merged-fx [k v]]
(into #{} (keys new-fx)))] (if (= :db k)
(if (empty? (set/difference common-keys (conj mergable-keys :db))) (assoc merged-fx :db v)
(clojure.core/merge (apply dissoc fx mergable-keys) (if (get merged-fx k)
(apply dissoc new-fx mergable-keys) (if (mergeable-keys k)
(merge-with into (update merged-fx k into v)
(select-keys fx mergable-keys) (do (log/error "Merging fx with common-key: " k v)
(select-keys new-fx mergable-keys))) (reduced {:merging-fx-with-common-keys k})))
(do (log/error "Merging fx with common-keys: " common-keys) (assoc merged-fx k v))))
{:merging-fx-with-common-keys common-keys}))))) fx
new-fx)))
(defn merge (defn merge
"Takes a map of co-effects and forms as argument. "Takes a map of co-effects and forms as argument.
@ -51,10 +52,10 @@
(let [[first-arg & rest-args] args (let [[first-arg & rest-args] args
initial-fxs? (map? first-arg) initial-fxs? (map? first-arg)
fx-fns (if initial-fxs? rest-args args)] fx-fns (if initial-fxs? rest-args args)]
(reduce (fn [fxs fx-fn] (clojure.core/reduce (fn [fxs fx-fn]
(let [updated-cofx (update-db cofx fxs)] (let [updated-cofx (update-db cofx fxs)]
(if fx-fn (if fx-fn
(safe-merge fxs (fx-fn updated-cofx)) (safe-merge fxs (fx-fn updated-cofx))
fxs))) fxs)))
(if initial-fxs? first-arg {:db db}) (if initial-fxs? first-arg {:db db})
fx-fns))) fx-fns)))