From dd52e5dfff8ed0c057a02990a25f7e1a7ec4c75f Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 22 Aug 2021 17:03:48 +0300 Subject: [PATCH 01/11] m/-set-children doesn't recreate identical schemas * identity walking is order(s) of magnitude faster --- perf/malli/creation_perf_test.cljc | 30 +++++++++++++++++++++--------- src/malli/core.cljc | 7 +++++-- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/perf/malli/creation_perf_test.cljc b/perf/malli/creation_perf_test.cljc index 52b0f550d..5aa6216d5 100644 --- a/perf/malli/creation_perf_test.cljc +++ b/perf/malli/creation_perf_test.cljc @@ -4,12 +4,15 @@ [malli.core :as m] [malli.util :as mu])) +(defmacro bench [& body] + `(cc/quick-bench ~@body)) + (defmacro profile [& body] `(let [start# (System/currentTimeMillis)] (dotimes [_# 100000] ~@body) (let [ms# (- (System/currentTimeMillis) start#) times# (int (/ 1000000000 ms#))] - (print "invoking" times# "times") + (println "invoking" times# "times") (time (prof/profile (dotimes [_# times#] ~@body)))))) (defmacro profile-for @@ -48,21 +51,21 @@ ;; ;; 5.2µs - (cc/quick-bench (m/validate [:or :int :string] 42)) + (bench (m/validate [:or :int :string] 42)) (profile (m/validate [:or :int :string] 42)) ;; 3.0µs - (cc/quick-bench (m/schema [:or :int :string])) + (bench (m/schema [:or :int :string])) (profile (m/schema [:or :int :string])) ;; 1.7µs (let [schema (m/schema [:or :int :string])] - (cc/quick-bench (m/validator schema)) + (bench (m/validator schema)) #_(profile (m/validator schema))) ;; 4ns (let [validate (m/validator [:or :int :string])] - (cc/quick-bench (validate 42)) + (bench (validate 42)) #_(profile (validate 42)))) (def ?schema @@ -75,6 +78,8 @@ (def schema (m/schema ?schema)) +(def leaf-schema (m/schema :int)) + (comment ;; @@ -82,11 +87,11 @@ ;; ;; 480ns - (cc/quick-bench (m/schema :int)) + (bench (m/schema :int)) (profile (m/schema :int)) ;; 44µs - (cc/quick-bench (m/schema ?schema)) + (bench (m/schema ?schema)) (profile (m/schema ?schema))) (comment @@ -95,12 +100,19 @@ ;; schema transformation ;; + ;; 271ns + ;; 14ns (-set-children, -set-properties) + (bench (m/walk leaf-schema (m/schema-walker identity))) + (profile (m/walk leaf-schema (m/schema-walker identity))) + ;; 26µs - (cc/quick-bench (m/walk schema (m/schema-walker identity))) + ;; 1.3µs (-set-children, -set-properties) + (bench (m/walk schema (m/schema-walker identity))) (profile (m/walk schema (m/schema-walker identity))) ;; 51µs - (cc/quick-bench (mu/closed-schema schema)) + ;; 44µs (-set-children, -set-properties) + (bench (mu/closed-schema schema)) (profile (mu/closed-schema schema))) (comment diff --git a/src/malli/core.cljc b/src/malli/core.cljc index d7fad3c86..2fe74e319 100644 --- a/src/malli/core.cljc +++ b/src/malli/core.cljc @@ -170,6 +170,8 @@ (defn -update [m k f] (assoc m k (f (get m k)))) +(defn -equals [x y] (or (identical? x y) (= x y))) + (defn -memoize [f] (let [value #?(:clj (AtomicReference. nil), :cljs (atom nil))] (fn [] #?(:clj (or (.get value) (do (.set value (f)) (.get value))), :cljs (or @value (reset! value (f))))))) @@ -181,7 +183,8 @@ (mapv (fn [[k s]] [k (-properties s) (-inner walker s (conj path k) options)]) entries)) (defn -set-children [schema children] - (-into-schema (-parent schema) (-properties schema) children (-options schema))) + (if (-equals children (-children schema)) + schema (-into-schema (-parent schema) (-properties schema) children (-options schema)))) (defn -update-options [schema f] (-into-schema (-parent schema) (-properties schema) (-children schema) (f (-options schema)))) @@ -398,7 +401,7 @@ (-intercepting (-value-transformer transformer this method options))) (-walk [this walker path options] (if (-accept walker this path options) - (-outer walker this path (vec children) options))) + (-outer walker this path children options))) (-properties [_] properties) (-options [_] options) (-children [_] children) From 42a3e64c01b5fad11faf45d2958c09570c8bf7c3 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 22 Aug 2021 17:04:13 +0300 Subject: [PATCH 02/11] cleanup --- src/malli/core.cljc | 4 ++++ src/malli/util.cljc | 6 +----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/malli/core.cljc b/src/malli/core.cljc index 2fe74e319..5d925c6e1 100644 --- a/src/malli/core.cljc +++ b/src/malli/core.cljc @@ -186,6 +186,10 @@ (if (-equals children (-children schema)) schema (-into-schema (-parent schema) (-properties schema) children (-options schema)))) +(defn -set-properties [schema properties] + (if (-equals properties (-properties schema)) + schema (-into-schema (-parent schema) properties (-children schema) (-options schema)))) + (defn -update-options [schema f] (-into-schema (-parent schema) (-properties schema) (-children schema) (f (-options schema)))) diff --git a/src/malli/util.cljc b/src/malli/util.cljc index a5f389cc3..2cf60baca 100644 --- a/src/malli/util.cljc +++ b/src/malli/util.cljc @@ -116,11 +116,7 @@ "Returns a Schema instance with updated properties." [?schema f & args] (let [schema (m/schema ?schema)] - (m/into-schema - (m/-parent schema) - (not-empty (apply f (m/-properties schema) args)) - (m/-children schema) - (m/-options schema)))) + (m/-set-properties schema (not-empty (apply f (m/-properties schema) args))))) (defn closed-schema "Closes recursively all :map schemas by adding `{:closed true}` From af0efd08a5d6ff6943bebaaacb15d7dc063a6c62 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 22 Aug 2021 23:59:36 +0300 Subject: [PATCH 03/11] faster m/-create-form --- perf/malli/creation_perf_test.cljc | 7 +++++++ src/malli/core.cljc | 10 +++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/perf/malli/creation_perf_test.cljc b/perf/malli/creation_perf_test.cljc index 5aa6216d5..e22ee8bde 100644 --- a/perf/malli/creation_perf_test.cljc +++ b/perf/malli/creation_perf_test.cljc @@ -115,6 +115,13 @@ (bench (mu/closed-schema schema)) (profile (mu/closed-schema schema))) +(comment + + (let [t ::or, p {:a 1}, c (mapv m/schema [:int :int])] + ;; 480ns + ;; 221ns (faster impl) + (bench (m/-create-form t p c)))) + (comment (prof/serve-files 8080) (prof/clear-results)) diff --git a/src/malli/core.cljc b/src/malli/core.cljc index 5d925c6e1..0d0a12544 100644 --- a/src/malli/core.cljc +++ b/src/malli/core.cljc @@ -135,11 +135,11 @@ (-fail! ::child-error (merge {:type type, :properties properties, :children children} opts)))) (defn -create-form [type properties children] - (cond - (and (seq properties) (seq children)) (into [type properties] children) - (seq properties) [type properties] - (seq children) (into [type] children) - :else type)) + (let [has-children (seq children), has-properties (seq properties)] + (cond (and has-properties has-children) (reduce conj [type properties] children) + has-properties [type properties] + has-children (reduce conj [type] children) + :else type))) (defn -pointer [id schema options] (-into-schema (-schema-schema {:id id}) nil [schema] options)) From c97ade630f6f7e9876e5e22af7a0787b5bca675b Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 23 Aug 2021 08:43:15 +0300 Subject: [PATCH 04/11] faster -val-schema --- perf/malli/creation_perf_test.cljc | 9 +++++++++ src/malli/core.cljc | 23 ++++++++++++----------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/perf/malli/creation_perf_test.cljc b/perf/malli/creation_perf_test.cljc index e22ee8bde..a503386c7 100644 --- a/perf/malli/creation_perf_test.cljc +++ b/perf/malli/creation_perf_test.cljc @@ -122,6 +122,15 @@ ;; 221ns (faster impl) (bench (m/-create-form t p c)))) +(comment + (let [s (m/schema :int)] + ;; 440ns + ;; 341ns (-create-form) + ;; 150ns (delayed form) + ;; 30ns (don't -check-children) + (bench (m/-val-schema s nil)) + (profile (m/-val-schema s nil)))) + (comment (prof/serve-files 8080) (prof/clear-results)) diff --git a/src/malli/core.cljc b/src/malli/core.cljc index 0d0a12544..11f4dc000 100644 --- a/src/malli/core.cljc +++ b/src/malli/core.cljc @@ -130,9 +130,10 @@ (defn -unlift-keys [m prefix] (reduce-kv #(if (= (name prefix) (namespace %2)) (assoc %1 (keyword (name %2)) %3) %1) {} m)) -(defn -check-children! [type properties children {:keys [min max] :as opts}] - (if (or (and min (< (count children) min)) (and max (> (count children) max))) - (-fail! ::child-error (merge {:type type, :properties properties, :children children} opts)))) +(defn -check-children! [type properties children opts] + (let [size (count children), min (:min opts 0), max (:max opts size)] + (when (or (< size min) (> size max)) + (-fail! ::child-error {:type type, :properties properties, :children children, :min min, :max max})))) (defn -create-form [type properties children] (let [has-children (seq children), has-properties (seq properties)] @@ -640,7 +641,7 @@ (defn -val-schema ([schema properties] - (-into-schema (-val-schema) properties [schema] (-options schema))) + (-into-schema (-val-schema) properties (list schema) (-options schema))) ([] ^{:type ::into-schema} (reify IntoSchema @@ -649,9 +650,9 @@ (-properties-schema [_ _]) (-children-schema [_ _]) (-into-schema [parent properties children options] - (-check-children! ::val properties children {:min 1, :max 1}) - (let [[schema :as children] (map #(schema % options) children) - form (-create-form ::val properties (map -form children))] + #_(-check-children! ::val properties children {:min 1, :max 1}) + (let [schema (schema (first children) options) + form (delay (-create-form ::val properties [(-form schema)]))] ^{:type ::schema} (reify Schema (-validator [_] (-validator schema)) @@ -659,17 +660,17 @@ (-parser [_] (-parser schema)) (-unparser [_] (-unparser schema)) (-transformer [this transformer method options] - (-parent-children-transformer this children transformer method options)) + (-parent-children-transformer this (list schema) transformer method options)) (-walk [this walker path options] (if (::walk-entry-vals options) (if (-accept walker this path options) - (-outer walker this path [(-inner walker schema path options)] options)) + (-outer walker this path (list (-inner walker schema path options)) options)) (-walk schema walker path options))) (-properties [_] properties) (-options [_] (-options schema)) - (-children [_] children) + (-children [_] [schema]) (-parent [_] parent) - (-form [_] form) + (-form [_] @form) LensSchema (-keep [_]) (-get [_ key default] (if (= 0 key) schema default)) From 0e0632a0834ccc6b18c332d8bbe8e7e13b4d9a2d Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 23 Aug 2021 09:14:38 +0300 Subject: [PATCH 05/11] fix -check-children --- src/malli/core.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/malli/core.cljc b/src/malli/core.cljc index 11f4dc000..99aa41402 100644 --- a/src/malli/core.cljc +++ b/src/malli/core.cljc @@ -131,7 +131,7 @@ (reduce-kv #(if (= (name prefix) (namespace %2)) (assoc %1 (keyword (name %2)) %3) %1) {} m)) (defn -check-children! [type properties children opts] - (let [size (count children), min (:min opts 0), max (:max opts size)] + (let [size (count children), min (or (:min opts) 0), max (or (:max opts) size)] (when (or (< size min) (> size max)) (-fail! ::child-error {:type type, :properties properties, :children children, :min min, :max max})))) From 6d108e4b5c8bf3599f96167ff95d490ff0eb28fb Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 23 Aug 2021 09:14:48 +0300 Subject: [PATCH 06/11] polish registry-code --- src/malli/registry.cljc | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/malli/registry.cljc b/src/malli/registry.cljc index 9fb6cf74a..219be725e 100644 --- a/src/malli/registry.cljc +++ b/src/malli/registry.cljc @@ -14,19 +14,11 @@ (-schema [_ type] (schemas type)) (-schemas [_] schemas))) -(defn registry - [?registry] - #?(:clj - (when ?registry - (if (instance? malli.registry.Registry ?registry) - ?registry - (if (map? ?registry) - (simple-registry ?registry) - (when (satisfies? Registry ?registry) - ?registry)))) - :cljs - (cond (satisfies? Registry ?registry) ?registry - (map? ?registry) (simple-registry ?registry)))) +(defn registry [?registry] + (cond (nil? ?registry) nil + #?@(:clj [(instance? malli.registry.Registry ?registry) ?registry]) + (map? ?registry) (simple-registry ?registry) + (satisfies? Registry ?registry) ?registry)) ;; ;; custom From 3e3e14cff40834614c10179fabc462230765c53c Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 23 Aug 2021 09:16:07 +0300 Subject: [PATCH 07/11] tighten --- src/malli/core.cljc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/malli/core.cljc b/src/malli/core.cljc index 99aa41402..a956b1e43 100644 --- a/src/malli/core.cljc +++ b/src/malli/core.cljc @@ -261,10 +261,7 @@ (defn -registry {:arglists '([] [{:keys [registry]}])} ([] default-registry) - ([opts] - (or (when opts - (mr/registry (opts :registry))) - default-registry))) + ([opts] (or (when opts (mr/registry (opts :registry))) default-registry))) (defn- -lookup [?schema options] (let [registry (-registry options)] From d9128ef3d241bafbda097268b9d70a522d2a9f39 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 23 Aug 2021 12:29:12 +0300 Subject: [PATCH 08/11] partial cherry-pick from #521 --- src/malli/core.cljc | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/malli/core.cljc b/src/malli/core.cljc index a956b1e43..db2df1524 100644 --- a/src/malli/core.cljc +++ b/src/malli/core.cljc @@ -246,11 +246,6 @@ child-transformer (if (seq child-transformers) (apply -comp (rseq child-transformers)))] (-intercepting parent-transformer child-transformer))) -(defn- -properties-and-children [[x :as xs]] - (if (or (nil? x) (map? x)) - [x (rest xs)] - [nil xs])) - (defn- -register-var [registry v] (let [name (-> v meta :name) schema (-simple-schema {:type name, :pred @v})] @@ -475,8 +470,8 @@ (-children-schema [_ _]) (-into-schema [parent properties children options] (-check-children! :or properties children {:min 1}) - (let [children (mapv #(schema % options) children) - form (-create-form :or properties (map -form children)) + (let [children (map #(schema % options) children) + form (delay (-create-form :or properties (map -form children))) ->parser (fn [f] (let [parsers (mapv f children)] #(reduce (fn [_ parser] (miu/-map-valid reduced (parser %))) ::invalid parsers)))] ^{:type ::schema} @@ -521,7 +516,7 @@ (-options [_] options) (-children [_] children) (-parent [_] parent) - (-form [_] form) + (-form [_] @form) LensSchema (-keep [_]) (-get [_ key default] (get children key default)) @@ -1577,8 +1572,11 @@ ([type properties children] (into-schema type properties children nil)) ([type properties children options] - (let [[properties options] (-properties-and-options properties options -form)] - (-into-schema (-schema type options) (if (seq properties) properties) children options)))) + (let [properties (when properties (when (pos? (count properties)) properties)) + r (when properties (properties :registry)) + options (if r (-update options :registry #(mr/composite-registry r (or % (-registry options)))) options) + properties (if r (assoc properties :registry (-property-registry r options -form)) properties)] + (-into-schema (-schema type options) properties children options)))) (defn type "Returns the Schema type." @@ -1633,8 +1631,14 @@ (cond (schema? ?schema) ?schema (into-schema? ?schema) (-into-schema ?schema nil nil options) - (vector? ?schema) (let [[p c] (-properties-and-children (rest ?schema))] - (into-schema (-schema (first ?schema) options) p c options)) + (vector? ?schema) (let [t (nth ?schema 0) + n (count ?schema) + ?p (when (> n 1) (nth ?schema 1))] + (if-let [p (when (or (nil? ?p) (map? ?p)) ?p)] + (let [c (when (< 2 n) (subvec ?schema 2 n))] + (into-schema (-schema t options) p c options)) + (let [c (when (< 1 n) (subvec ?schema 1 n))] + (into-schema (-schema t options) nil c options)))) :else (if-let [?schema' (and (-reference? ?schema) (-lookup ?schema options))] (-pointer ?schema (schema ?schema' options) options) (-> ?schema (-schema options) (schema options)))))) From c9f47985b6b1724841ec68e0ffdb1e3e83fe27c6 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 23 Aug 2021 12:36:45 +0300 Subject: [PATCH 09/11] some numbers --- perf/malli/creation_perf_test.cljc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/perf/malli/creation_perf_test.cljc b/perf/malli/creation_perf_test.cljc index a503386c7..30ae52829 100644 --- a/perf/malli/creation_perf_test.cljc +++ b/perf/malli/creation_perf_test.cljc @@ -86,11 +86,11 @@ ;; schema creation ;; - ;; 480ns + ;; 480ns -> 400ns (bench (m/schema :int)) (profile (m/schema :int)) - ;; 44µs + ;; 44µs -> 31µs (bench (m/schema ?schema)) (profile (m/schema ?schema))) @@ -112,6 +112,7 @@ ;; 51µs ;; 44µs (-set-children, -set-properties) + ;; 29µs (lot's of stuff) (bench (mu/closed-schema schema)) (profile (mu/closed-schema schema))) From 32068c650b9d658af39c001e085f84be9f19135f Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 23 Aug 2021 13:11:02 +0300 Subject: [PATCH 10/11] fix --- perf/malli/creation_perf_test.cljc | 3 +++ src/malli/core.cljc | 13 ++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/perf/malli/creation_perf_test.cljc b/perf/malli/creation_perf_test.cljc index 30ae52829..cb0eee9db 100644 --- a/perf/malli/creation_perf_test.cljc +++ b/perf/malli/creation_perf_test.cljc @@ -51,10 +51,13 @@ ;; ;; 5.2µs + ;; 3.6µs (bench (m/validate [:or :int :string] 42)) (profile (m/validate [:or :int :string] 42)) ;; 3.0µs + ;; 500ns (delayed mapv childs) + ;; 1.7µs (bench (m/schema [:or :int :string])) (profile (m/schema [:or :int :string])) diff --git a/src/malli/core.cljc b/src/malli/core.cljc index db2df1524..067565a3f 100644 --- a/src/malli/core.cljc +++ b/src/malli/core.cljc @@ -470,7 +470,7 @@ (-children-schema [_ _]) (-into-schema [parent properties children options] (-check-children! :or properties children {:min 1}) - (let [children (map #(schema % options) children) + (let [children (mapv #(schema % options) children) form (delay (-create-form :or properties (map -form children))) ->parser (fn [f] (let [parsers (mapv f children)] #(reduce (fn [_ parser] (miu/-map-valid reduced (parser %))) ::invalid parsers)))] @@ -1633,12 +1633,11 @@ (into-schema? ?schema) (-into-schema ?schema nil nil options) (vector? ?schema) (let [t (nth ?schema 0) n (count ?schema) - ?p (when (> n 1) (nth ?schema 1))] - (if-let [p (when (or (nil? ?p) (map? ?p)) ?p)] - (let [c (when (< 2 n) (subvec ?schema 2 n))] - (into-schema (-schema t options) p c options)) - (let [c (when (< 1 n) (subvec ?schema 1 n))] - (into-schema (-schema t options) nil c options)))) + ?p (when (> n 1) (nth ?schema 1)) + s (-schema t options)] + (if (or (nil? ?p) (map? ?p)) + (into-schema s ?p (when (< 2 n) (subvec ?schema 2 n)) options) + (into-schema s nil (when (< 1 n) (subvec ?schema 1 n)) options))) :else (if-let [?schema' (and (-reference? ?schema) (-lookup ?schema options))] (-pointer ?schema (schema ?schema' options) options) (-> ?schema (-schema options) (schema options)))))) From 2a242535560fab13d52527fbf86564e3d36fca74 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 23 Aug 2021 13:40:38 +0300 Subject: [PATCH 11/11] . --- src/malli/core.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/malli/core.cljc b/src/malli/core.cljc index 067565a3f..9114a3b5d 100644 --- a/src/malli/core.cljc +++ b/src/malli/core.cljc @@ -131,8 +131,8 @@ (reduce-kv #(if (= (name prefix) (namespace %2)) (assoc %1 (keyword (name %2)) %3) %1) {} m)) (defn -check-children! [type properties children opts] - (let [size (count children), min (or (:min opts) 0), max (or (:max opts) size)] - (when (or (< size min) (> size max)) + (let [size (count children), min (:min opts), max (:max opts)] + (when (or (and min (< size min)) (and max (> size max))) (-fail! ::child-error {:type type, :properties properties, :children children, :min min, :max max})))) (defn -create-form [type properties children]