Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable (leaf-)Schema caching #870

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
:perf {:extra-paths ["perf"]
:extra-deps {criterium/criterium {:mvn/version "0.4.6"}
org.clojure/clojure {:mvn/version "1.11.1"}
com.clojure-goes-fast/clj-memory-meter {:mvn/version "0.2.2"}
com.clojure-goes-fast/clj-async-profiler {:mvn/version "1.0.3"}}
:jvm-opts ["-server"
"-Xmx4096m"
Expand Down
11 changes: 7 additions & 4 deletions perf/malli/perf/core.cljc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
(ns malli.perf.core
(:require [criterium.core :as cc]
[clj-memory-meter.core :as mm]
[clj-async-profiler.core :as prof]))

(defn serve! []
Expand All @@ -13,16 +14,18 @@
`(cc/quick-bench ~@body))

(defmacro profile [& body]
`(let [start# (System/currentTimeMillis)]
`(let [start# (System/nanoTime)]
(dotimes [_# 10000] ~@body)
(let [ms# (- (System/currentTimeMillis) start#)
times# (int (/ 100000000 ms#))]
(let [ns# (- (System/nanoTime) start#)
times# (long (/ 100000000000000 ns#))]
(println "invoking" times# "times")
(time (prof/profile (dotimes [_# times#] ~@body))))))
(time (prof/profile {:event :cpu} (dotimes [_# times#] ~@body))))))

(defmacro bench [& body]
`(do (serve!) (-bench ~@body) (profile ~@body)))

(def measure mm/measure)

(comment (clear!))

(defmacro profile-for
Expand Down
45 changes: 44 additions & 1 deletion perf/malli/perf/creation_perf_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@
;; 2.5µs (...)
;; 2.3µs (-vmap, don't check children)
;; 1.1µs
;; 270ns (M1-JDK17)
(p/bench (m/validate [:or :int :string] 42))

;; 2.6µs
;; 1.3µs
;; 380ns (M1-JDK17)
(p/bench (m/validate (m/from-ast {:type :or, :children [{:type :int} {:type :string}]}) 42))

;; 15ns
;; 7.2ns (M1-JDK17)
(let [schema (m/schema [:or :int :string])]
(p/bench (m/validate schema 42)))

Expand All @@ -37,9 +40,12 @@
;; 1.1µs (mapv childs)
;; 750ns (...)
;; 680ns (-vmap, don't check children)
;; 180ns (M1-JDK17)
;; 160ns (-type-cache)
(p/bench (m/schema [:or :int :string]))

;; 730ns
;; 230ns (M1-JDK17)
(p/bench (m/from-ast {:type :or, :children [{:type :int} {:type :string}]}))

;; 1.7µs
Expand All @@ -50,23 +56,29 @@
;; 1.1µs (mapv childs)
;; 750ns (...)
;; 680ns (-vmap, don't check children)
;; 190ns (M1-JDK17)
;; 160ns (-type-cache)
(p/bench (m/schema [:and :int :string]))

;; 730ns
;; 240ns (M1-JDK17)
(p/bench (m/from-ast {:type :and, :children [{:type :int} {:type :string}]}))

;; 1.7µs
;; 1.5µs (fast parse)
;; 540ns (non-distinct)
;; 13ns (-cache)
;; 6.5ns (M1-JDK17)
(let [schema (m/schema [:or :int :string])]
(p/bench (m/validator schema)))

;; 16ns
;; 7.0ns (M1-JDK17)
(let [schema (m/schema [:or :int :string])]
(p/bench (m/validate schema 42)))

;; 3ns
;; 2.3ns (M1-JDK17)
(let [validate (m/validator [:or :int :string])]
(p/bench (validate 42))))

Expand All @@ -91,47 +103,67 @@
;;

;; 480ns -> 400ns -> 340ns -> 280ns -> 240ns -> 170ns (registry) -> 160ns (recur)
;; 55ns (M1-JDK17)
;; 34ns (-type-cache)
(p/bench (m/schema :int))

;; 180ns
;; 78ns (M1-JDK17)
(p/bench (m/from-ast {:type :int}))

;; 44µs -> 31µs -> 18µs -> 11µs -> 9.4µs -> 9.0µs -> 8.5µs -> 7.0µs -> 6.4µs (registry) -> 5.7µs
;; 3.4µs
;; 2.9µs (-entry-parser)
;; 2.5µs (no entries, object-arraus)
;; 1.0µs (M1-JDK17)
;; 840ns (-type-cache)
(p/bench (m/schema ?schema))

;; 44µs -> 240ns
;; 110ns (M1-JDK17)
;; 95ns (-type-cache)
(p/bench (m/schema ?schema {::m/lazy-entries true}))

;; 147ns
;; 84ns (M1-JDK17)
(p/bench (m/from-ast ast))

;; 3.7µs
;; 1.4µs (M1-JDK17)
;; 1.3ns (-type-cache)
(p/bench (m/validator (m/schema ?schema)))

;; 2.5µs
;; 900ns (M1-JDK17)
(p/bench (m/validator (m/from-ast ast)))

;; 1.6µs -> 64ns
;; 40ns (M1-JDK17)
(p/bench (m/validate schema {:x true, :z {:x true}}))

;; 1.6µs -> 450ns
;; 120ns (M1-JDK17)
(p/bench (m/explain schema {:x true, :z {:x true}}))

;; does not work with direct linking
(with-redefs [m/-check-children? (constantly false)]
(p/bench (m/schema ?schema))))
(p/bench (m/schema ?schema)))

;; memory allocation, THIS IS NOT CORRECT as it follows parents, which are shared
(p/measure (m/schema ?schema)) ;; => 5.7KiB

)

(def ref-schema (m/schema [:schema :int]))

(comment

;; 14ns -> 5ns
;; 3.3ns (M1-JDK17)
(p/bench (m/deref ref-schema))

;; 5µs -> 28ns
;; 10ns (M1-JDK17)
(p/bench (m/deref-all ref-schema)))

(comment
Expand All @@ -143,11 +175,13 @@
;; 271ns
;; 14ns (-set-children, -set-properties)
;; 12ns (-entry-parser)
;; 7.2ns (M1-JDK17)
(p/bench (m/walk leaf-schema (m/schema-walker identity)))

;; 26µs
;; 1.3µs (-set-children, -set-properties)
;; 1.2µs (protocols, registry, recur)
;; 700ns (M1-JDK17)
(p/bench (m/walk schema (m/schema-walker identity)))

;; 51µs
Expand All @@ -161,45 +195,53 @@
;; 3.9µs (-parsed)
;; 3.6µs (-entry-parser)
;; 3.4µs (object-array)
;; 1.4µs (M1-JDK17)
(p/bench (mu/closed-schema schema))

;; 3.8µs
;; 3.4µs (satisfies?)
;; 2.2µs (-set-entries)
;; 830ns (-update-parsed)
;; 560ns (-entry-parser)
;; 190ns (M1-JDK17)
(p/bench (mu/assoc schema :y :string))

;; 4.2µs
;; 3.8µs (satisfies?)
;; 820ns (-update-parsed)
;; 540ns (-entry-parser)
;; 180ns (M1-JDK17)
(p/bench (mu/assoc schema :w :string))

;; 205ns
;; 195ns
;; 73ns (M1-JDK17)
(p/bench (mu/get schema :y))

;; 13µs
;; 2.4µs (satisfies?)
;; 1.8µs
;; 690ns (M1-JDK17)
(p/bench (mu/required-keys schema))

;; 134µs
;; 15µs (satisfies?)
;; 9µs (fast merge)
;; 2.7µs (M1-JDK17)
(p/bench (mu/merge schema schema)))

(comment
;; 119µs
;; 16µs (cache generator)
;; 4.6µs (M1-JDK17)
(p/bench (mg/generate schema)))

(comment

(let [t ::or, p {:a 1}, c (mapv m/schema [:int :int])]
;; 480ns
;; 221ns (faster impl)
;; 120ns (M1-JDK17)
(p/bench (m/-create-form t p c nil))))

(comment
Expand All @@ -208,6 +250,7 @@
;; 341ns (-create-form)
;; 150ns (delayed form)
;; 30ns (don't -check-children)
;; 120ns (M1-JDK17)
(p/bench (m/-val-schema s nil))))

(comment
Expand Down
15 changes: 10 additions & 5 deletions src/malli/core.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -619,8 +619,9 @@
;;

(defn -simple-schema [props]
(let [{:keys [type type-properties pred property-pred min max from-ast to-ast compile]
:or {min 0, max 0, from-ast -from-value-ast, to-ast -to-type-ast}} props]
(let [{:keys [type type-properties pred property-pred min max from-ast to-ast compile options]
:or {min 0, max 0, from-ast -from-value-ast, to-ast -to-type-ast}} props
type-cache (-create-cache options)]
(if (fn? props)
(do
(-deprecated! "-simple-schema doesn't take fn-props, use :compiled property instead")
Expand All @@ -629,6 +630,8 @@
(reify
AST
(-from-ast [parent ast options] (from-ast parent ast options))
Cached
(-cache [_] type-cache)
IntoSchema
(-type [_] type)
(-type-properties [_] type-properties)
Expand Down Expand Up @@ -2003,9 +2006,9 @@
[x] (#?(:clj instance?, :cljs implements?) malli.core.Schema x))

(defn schema
"Creates a Schema object from any of the following:
"Creates a Schema instance from any of the following:

- Schema instance (just returns it)
- Schema instance
- IntoSchema instance
- Schema vector syntax, e.g. [:string {:min 1}]
- Qualified Keyword or String, using a registry lookup"
Expand All @@ -2014,7 +2017,9 @@
([?schema options]
(cond
(schema? ?schema) ?schema
(into-schema? ?schema) (-into-schema ?schema nil nil options)
(into-schema? ?schema) (if (nil? options)
(-cached ?schema :schema #(-into-schema % nil nil nil))
(-into-schema ?schema nil nil options))
(vector? ?schema) (let [v #?(:clj ^IPersistentVector ?schema, :cljs ?schema)
t (-lookup! #?(:clj (.nth v 0), :cljs (nth v 0)) into-schema? true options)
n #?(:bb (count v) :clj (.count v), :cljs (count v))
Expand Down