Skip to content

Instantly share code, notes, and snippets.

@stevebuik
Last active December 27, 2020 06:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stevebuik/e63735d99fca94041120f9b0e25b616d to your computer and use it in GitHub Desktop.
Save stevebuik/e63735d99fca94041120f9b0e25b616d to your computer and use it in GitHub Desktop.
example of Malli tree nodes using a :multi for the nodes
(ns recursive-multi-malli
(:require [malli.core :as m]
[malli.util :as mu]
[malli.error :as me]
[criterium.core :as criterium]))
(def simple-tree-node
[:schema {:registry {::type [:enum :blue :green]
::children [:vector [:ref ::node]]
::node [:multi {:dispatch :type}
[:blue [:map
[:type {:optional false} ::type]
[:children {:optional true} ::children]
[:name string?]
[:count pos-int?]
]]
[:green [:map
[:type {:optional false} ::type]
[:children {:optional true} ::children]
[:name string?]
[:size pos-int?]]]]}}
::node])
(def sample1 {:type :blue
:name "foo"
:count 2})
(def sample2 {:type :blue
:name "foo"
:count 2
:children [{:type :green
:name "bar"
:size 4}]})
(def sample3 {:type :blue
:name "foo"
:count 2
:children [{:type :green
:name "bar"
:size 4}
{:type :green
:name "bar"
:size 4}]})
(def sample4 {:type :blue
:name "foo"
:count 2
:children [{:type :green
:name "bar"
:size 4}
{:type :green
:name "bar"
:size 4}
{:type :green
:name "bar"
:size 4}]})
(def simple-tree-node-merged
[:schema {:registry {::type [:enum :blue :green]
::children [:vector [:ref ::node]]
::common [:map
[:type {:optional false} ::type]
[:children {:optional true} ::children]
[:name string?]]
::node [:multi {:dispatch :type}
[:blue [:merge ::common
[:map
[:count pos-int?]]]]
[:green [:merge ::common
[:map
[:size pos-int?]]]]]}}
::node])
(comment
; check validation works
(clojure.pprint/pprint (me/humanize (m/explain simple-tree-node sample1)))
; without children
(let [validator (m/validator simple-tree-node)]
(criterium/quick-bench (validator sample1)))
; 15 x slower with one child
(let [validator (m/validator simple-tree-node)]
(criterium/quick-bench (validator sample2)))
; 30 x slower with 3 children
(let [validator (m/validator simple-tree-node)]
(criterium/quick-bench (validator sample3)))
; 45 x slower with 4 children
(let [validator (m/validator simple-tree-node)]
(criterium/quick-bench (validator sample4)))
; 45 x slower with one child using :merge
(let [validator (m/validator simple-tree-node-merged {:registry (merge (m/default-schemas)
(mu/schemas))})]
(criterium/quick-bench (validator sample2)))
)
; observations:
; 1. combining recursion and multi is 2 x slower than recursion alone. see https://gist.github.com/stevebuik/e4f3475e46dd5ebb1de7707438fa073f
; 2. big perf slow-down when children are present
; 3. perf is then linear for deep or broad trees
; 4. using :merge instead of inline is 3 x slower
; Q: are there any further perf optimizations available?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment