From 9422cd28c1f32411e094f56adbaec419955416e1 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 9 Feb 2019 19:47:41 +0200 Subject: [PATCH] create empty path-parameters at creation time, 266ns->251ns (-6%) --- doc/images/opensensors.png | Bin 31497 -> 30778 bytes modules/reitit-core/java-src/reitit/Trie.java | 56 ++++++++------- modules/reitit-core/src/reitit/core.cljc | 4 +- modules/reitit-core/src/reitit/trie.cljc | 64 +++++++++--------- perf-test/clj/reitit/go_perf_test.clj | 6 +- .../clj/reitit/opensensors_perf_test.clj | 5 ++ perf-test/clj/reitit/perf_utils.clj | 2 +- test/cljc/reitit/trie_test.cljc | 8 +-- 8 files changed, 80 insertions(+), 65 deletions(-) diff --git a/doc/images/opensensors.png b/doc/images/opensensors.png index 9f65dff4a2b6560d5b78c573fda9e9107faf59b5..fd922808558bb073a55058c0e8f8bc1a29dbfc0f 100644 GIT binary patch delta 27293 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 b@F}J%y^YRk2{ m = new HashMap<>(); @@ -53,7 +58,7 @@ public class Trie { } public interface Matcher { - Match match(int i, int max, char[] path, Match match); + Match match(int i, int max, char[] path); int depth(); @@ -76,7 +81,7 @@ public class Trie { } @Override - public Match match(int i, int max, char[] path, Match match) { + public Match match(int i, int max, char[] path) { if (max < i + size) { return null; } @@ -85,7 +90,7 @@ public class Trie { return null; } } - return child.match(i + size, max, path, match); + return child.match(i + size, max, path); } @Override @@ -104,22 +109,23 @@ public class Trie { } } - public static DataMatcher dataMatcher(Object data) { - return new DataMatcher(data); + public static DataMatcher dataMatcher(IPersistentMap params, Object data) { + return new DataMatcher(params, data); } static final class DataMatcher implements Matcher { + private final IPersistentMap params; private final Object data; - DataMatcher(Object data) { + DataMatcher(IPersistentMap params, Object data) { + this.params = params; this.data = data; } @Override - public Match match(int i, int max, char[] path, Match match) { + public Match match(int i, int max, char[] path) { if (i == max) { - match.data = data; - return match; + return new Match(params, data); } return null; } @@ -156,7 +162,7 @@ public class Trie { } @Override - public Match match(int i, int max, char[] path, Match match) { + public Match match(int i, int max, char[] path) { if (i < max && path[i] != end) { int stop = max; for (int j = i; j < max; j++) { @@ -166,7 +172,7 @@ public class Trie { break; } } - final Match m = child.match(stop, max, path, match); + final Match m = child.match(stop, max, path); if (m != null) { m.params = m.params.assoc(key, decode(path, i, stop)); } @@ -191,25 +197,25 @@ public class Trie { } } - public static CatchAllMatcher catchAllMatcher(Keyword parameter, Object data) { - return new CatchAllMatcher(parameter, data); + public static CatchAllMatcher catchAllMatcher(Keyword parameter, IPersistentMap params, Object data) { + return new CatchAllMatcher(parameter, params, data); } static final class CatchAllMatcher implements Matcher { private final Keyword parameter; + private final IPersistentMap params; private final Object data; - CatchAllMatcher(Keyword parameter, Object data) { + CatchAllMatcher(Keyword parameter, IPersistentMap params, Object data) { this.parameter = parameter; + this.params = params; this.data = data; } @Override - public Match match(int i, int max, char[] path, Match match) { + public Match match(int i, int max, char[] path) { if (i < max) { - match.params = match.params.assoc(parameter, decode(path, i, max)); - match.data = data; - return match; + return new Match(params.assoc(parameter, decode(path, i, max)), data); } return null; } @@ -226,7 +232,7 @@ public class Trie { @Override public String toString() { - return "[" + parameter + " " + new DataMatcher(data) + "]"; + return "[" + parameter + " " + new DataMatcher(null, data) + "]"; } } @@ -246,9 +252,9 @@ public class Trie { } @Override - public Match match(int i, int max, char[] path, Match match) { + public Match match(int i, int max, char[] path) { for (int j = 0; j < size; j++) { - final Match m = childs[j].match(i, max, path, match); + final Match m = childs[j].match(i, max, path); if (m != null) { return m; } @@ -273,7 +279,7 @@ public class Trie { } public static Object lookup(Matcher matcher, String path) { - return matcher.match(0, path.length(), path.toCharArray(), new Match()); + return matcher.match(0, path.length(), path.toCharArray()); } public static void main(String[] args) { @@ -283,8 +289,8 @@ public class Trie { staticMatcher("/auth/", linearMatcher( Arrays.asList( - staticMatcher("login", dataMatcher(1)), - staticMatcher("recovery", dataMatcher(2))))))); + staticMatcher("login", dataMatcher(null, 1)), + staticMatcher("recovery", dataMatcher(null, 2))))))); System.err.println(matcher); System.out.println(lookup(matcher, "/auth/login")); System.out.println(lookup(matcher, "/auth/recovery")); diff --git a/modules/reitit-core/src/reitit/core.cljc b/modules/reitit-core/src/reitit/core.cljc index 9c05ba5f..fbab995f 100644 --- a/modules/reitit-core/src/reitit/core.cljc +++ b/modules/reitit-core/src/reitit/core.cljc @@ -131,7 +131,7 @@ (match-by-path [_ path] (if-let [match (trie/lookup matcher path)] (-> (:data match) - (assoc :path-params (:path-params match)) + (assoc :path-params (:params match)) (assoc :path path)))) (match-by-name [_ name] (if-let [match (impl/fast-get lookup name)] @@ -220,7 +220,7 @@ (match-by-path [_ path] (if-let [match (trie/lookup pl path)] (-> (:data match) - (assoc :path-params (:path-params match)) + (assoc :path-params (:params match)) (assoc :path path)))) (match-by-name [_ name] (if-let [match (impl/fast-get lookup name)] diff --git a/modules/reitit-core/src/reitit/trie.cljc b/modules/reitit-core/src/reitit/trie.cljc index 23228689..cd04da2e 100644 --- a/modules/reitit-core/src/reitit/trie.cljc +++ b/modules/reitit-core/src/reitit/trie.cljc @@ -7,8 +7,8 @@ (defrecord Wild [value]) (defrecord CatchAll [value]) -(defrecord Match [data path-params]) -(defrecord Node [children wilds catch-all data]) +(defrecord Match [params data]) +(defrecord Node [children wilds catch-all params data]) (defn wild? [x] (instance? Wild x)) (defn catch-all? [x] (instance? CatchAll x)) @@ -19,9 +19,9 @@ (depth [this]) (length [this])) -(defn assoc-path-param [match k v] - (let [params (:path-params match)] - (assoc match :path-params (assoc params k v)))) +(defn assoc-param [match k v] + (let [params (:params match)] + (assoc match :params (assoc params k v)))) ;; https://stackoverflow.com/questions/8033655/find-longest-common-prefix (defn common-prefix [s1 s2] @@ -121,25 +121,25 @@ ;; (defn- -node [m] - (map->Node (merge {:children {}, :wilds {}, :catch-all {}} m))) + (map->Node (merge {:children {}, :wilds {}, :catch-all {}, :params {}} m))) -(defn- -insert [node [path & ps] data] +(defn- -insert [node [path & ps] params data] (let [node' (cond (nil? path) - (assoc node :data data) + (assoc node :data data :params params) (instance? Wild path) (let [next (first ps)] (if (or (instance? Wild next) (instance? CatchAll next)) (ex/fail! (str "Two following wilds: " path ", " next)) - (update-in node [:wilds path] (fn [n] (-insert (or n (-node {})) ps data))))) + (update-in node [:wilds path] (fn [n] (-insert (or n (-node {})) ps params data))))) (instance? CatchAll path) - (assoc-in node [:catch-all path] (-node {:data data})) + (assoc-in node [:catch-all path] (-node {:params params, :data data})) (str/blank? path) - (-insert node ps data) + (-insert node ps params data) :else (or @@ -148,20 +148,20 @@ (if-let [cp (common-prefix p path)] (if (= cp p) ;; insert into child node - (let [n' (-insert n (conj ps (subs path (count p))) data)] + (let [n' (-insert n (conj ps (subs path (count p))) params data)] (reduced (assoc-in node [:children p] n'))) ;; split child node (let [rp (subs p (count cp)) rp' (subs path (count cp)) - n' (-insert (-node {}) ps data) - n'' (-insert (-node {:children {rp n, rp' n'}}) nil nil)] + n' (-insert (-node {}) ps params data) + n'' (-insert (-node {:children {rp n, rp' n'}}) nil nil nil)] (reduced (update node :children (fn [children] (-> children (dissoc p) (assoc cp n''))))))))) nil (:children node)) ;; new child node - (assoc-in node [:children path] (-insert (-node {}) ps data))))] + (assoc-in node [:children path] (-insert (-node {}) ps params data))))] (if-let [child (get-in node' [:children ""])] ;; optimize by removing empty paths (-> (merge-with merge (dissoc node' :data) child) @@ -173,9 +173,9 @@ (let [param (subs path start end)] (if percent? (js/decodeURIComponent param) param)))) -(defn data-matcher [data] - #?(:clj (Trie/dataMatcher data) - :cljs (let [match (->Match data {})] +(defn data-matcher [params data] + #?(:clj (Trie/dataMatcher params data) + :cljs (let [match (->Match params data)] (reify Matcher (match [_ i max _] (if (= i max) @@ -207,23 +207,23 @@ (loop [percent? false, j i] (if (= max j) (if-let [match (match matcher max max path)] - (assoc-path-param match key (decode path i max percent?))) + (assoc-param match key (decode path i max percent?))) (let [c ^char (get path j)] (condp = c end (if-let [match (match matcher j max path)] - (assoc-path-param match key (decode path i j percent?))) + (assoc-param match key (decode path i j percent?))) \% (recur true (inc j)) (recur percent? (inc j)))))))) (view [_] [key (view matcher)]) (depth [_] (inc (depth matcher))) (length [_])))) -(defn catch-all-matcher [key data] - #?(:clj (Trie/catchAllMatcher key data) - :cljs (let [match (->Match data nil)] +(defn catch-all-matcher [key params data] + #?(:clj (Trie/catchAllMatcher key params data) + :cljs (let [match (->Match params data)] (reify Matcher (match [_ i max path] - (if (< i max) (assoc-path-param match key (decode path i max true)))) + (if (< i max) (assoc-param match key (decode path i max true)))) (view [_] [key [data]]) (depth [_] 1) (length [_]))))) @@ -255,12 +255,14 @@ (insert acc p d)) node routes)) ([node path data] - (-insert (or node (-node {})) (split-path path) data))) + (let [parts (split-path path) + params (zipmap (->> parts (remove string?) (map :value)) (repeat nil))] + (-insert (or node (-node {})) (split-path path) params data)))) -(defn compile [{:keys [data children wilds catch-all]}] +(defn compile [{:keys [data params children wilds catch-all] :or {params {}}}] (let [ends (fn [{:keys [children]}] (or (keys children) ["/"])) matchers (-> [] - (cond-> data (conj (data-matcher data))) + (cond-> data (conj (data-matcher params data))) (into (for [[p c] children] (static-matcher p (compile c)))) (into (for [[p c] wilds] @@ -269,11 +271,11 @@ (if (next ends) (ex/fail! (str "Trie compliation error: wild " p " has two terminators: " ends)) (wild-matcher p (ffirst ends) (compile c)))))) - (into (for [[p c] catch-all] (catch-all-matcher (:value p) (:data c)))))] + (into (for [[p c] catch-all] (catch-all-matcher (:value p) params (:data c)))))] (cond (> (count matchers) 1) (linear-matcher matchers) (= (count matchers) 1) (first matchers) - :else (data-matcher nil)))) + :else (data-matcher {} nil)))) (defn pretty [matcher] #?(:clj (-> matcher str read-string eval) @@ -281,9 +283,9 @@ (defn lookup [matcher path] #?(:clj (if-let [match ^Trie$Match (Trie/lookup ^Trie$Matcher matcher ^String path)] - (->Match (.data match) (.params match))) + (->Match (.params match) (.data match))) :cljs (if-let [match (match matcher 0 (count path) path)] - (->Match (:data match) (:path-params match))))) + (->Match (:params match) (:data match))))) ;; ;; spike diff --git a/perf-test/clj/reitit/go_perf_test.clj b/perf-test/clj/reitit/go_perf_test.clj index 5f49558b..59838449 100644 --- a/perf-test/clj/reitit/go_perf_test.clj +++ b/perf-test/clj/reitit/go_perf_test.clj @@ -333,6 +333,7 @@ ;; 281ns (trie-router, no injects, optimized) ;; 277ns (trie-router, no injects, switch-case) - 690ns clojure ;; 273ns (trie-router, no injects, direct-data) + ;; 256ns (trie-router, pre-defined parameters) (let [req (map->Req {:request-method :get, :uri "/repos/julienschmidt/httprouter/stargazers"})] (title "param") (assert (= {:status 200, :body "/repos/:owner/:repo/stargazers"} (app req))) @@ -347,8 +348,9 @@ ;; 66µs (trie-router, no injects) ;; 64µs (trie-router, no injects, optimized) - 124µs (clojure) ;; 63µs (trie-router, no injects, switch-case) - 124µs (clojure) - ;; 63ns (trie-router, no injects, direct-data) - ;; 54ns (trie-router, non-transient params) + ;; 63µs (trie-router, no injects, direct-data) + ;; 54µs (trie-router, non-transient params) + ;; 49µs (trie-router, pre-defined parameters) (let [requests (mapv route->req routes)] (title "all") (cc/quick-bench diff --git a/perf-test/clj/reitit/opensensors_perf_test.clj b/perf-test/clj/reitit/opensensors_perf_test.clj index d74a9764..c00a89a6 100644 --- a/perf-test/clj/reitit/opensensors_perf_test.clj +++ b/perf-test/clj/reitit/opensensors_perf_test.clj @@ -573,6 +573,7 @@ ;; 194ns (trie) ;; 160ns (trie, prioritized) ;; 130ns (trie, non-transient, direct-data) + ;; 121ns (trie, pre-defined parameters) (b! "reitit" reitit-f) ;; 2845ns @@ -587,12 +588,14 @@ ;; 323ns (trie, prioritized) ;; 289ns (trie, prioritized, zero-copy) ;; 266ns (trie, non-transient, direct-data) + ;; 251ns (trie, pre-defined parameters) (b! "reitit-ring" reitit-ring-f) ;; 385ns (java-segment-router, no injects) ;; 271ms (trie) ;; 240ns (trie, prioritized) ;; 214ns (trie, non-transient, direct-data) + ;; 187ns (trie, pre-defined parameters) (b! "reitit-ring-fast" reitit-ring-fast-f) ;; 2553ns (linear-router) @@ -631,6 +634,7 @@ ;; 409ns (transient) ;; 409ns (staticMultiMatcher) ;; 305ns (non-persistent-params) + ;; 293ns (pre-defined parameters) (let [app (ring/ring-handler (ring/router opensensors-routes) {:inject-match? false, :inject-router? false}) request {:uri "/v1/users/1/devices/1", :request-method :get}] (doseq [[p r] (-> app (ring/get-router) (r/routes))] @@ -643,6 +647,7 @@ ; "Elapsed time: 9183.657012 msecs" ; "Elapsed time: 8674.70132 msecs" ; "Elapsed time: 6714.434915 msecs" + ; "Elapsed time: 6325.310043 msecs" (time (dotimes [_ 20000000] (app request))) diff --git a/perf-test/clj/reitit/perf_utils.clj b/perf-test/clj/reitit/perf_utils.clj index d2427903..49d0c485 100644 --- a/perf-test/clj/reitit/perf_utils.clj +++ b/perf-test/clj/reitit/perf_utils.clj @@ -62,7 +62,7 @@ (println) (let [times (for [[path [mean lower]] (bench-routes routes req f)] (do - (when verbose? (println (format "%7s %7s" mean lower) "\t" path)) + (when verbose? (println (format "%7s\t%7s" mean lower) "\t" path)) [mean lower]))] (title (str "average, lower/mean: " (int (/ (reduce + (map second times)) (count times))) "/" diff --git a/test/cljc/reitit/trie_test.cljc b/test/cljc/reitit/trie_test.cljc index b56981dd..291937c5 100644 --- a/test/cljc/reitit/trie_test.cljc +++ b/test/cljc/reitit/trie_test.cljc @@ -14,24 +14,24 @@ "/olipa/kerran/{*valvavan.suuri/avaruus}", "/olipa/kerran/{*valvavan.suuri/avaruus}")) (deftest tests - (is (= (trie/->Match {:a 1} {}) + (is (= (trie/->Match {} {:a 1}) (-> (trie/insert nil "/foo" {:a 1}) (trie/compile) (trie/lookup "/foo")))) - (is (= (trie/->Match {:a 1} {}) + (is (= (trie/->Match {} {:a 1}) (-> (trie/insert nil "/foo" {:a 1}) (trie/insert "/foo/*bar" {:b 1}) (trie/compile) (trie/lookup "/foo")))) - (is (= (trie/->Match {:b 1} {:bar "bar"}) + (is (= (trie/->Match {:bar "bar"} {:b 1}) (-> (trie/insert nil "/foo" {:a 1}) (trie/insert "/foo/*bar" {:b 1}) (trie/compile) (trie/lookup "/foo/bar")))) - (is (= (trie/->Match {:a 1} {}) + (is (= (trie/->Match {} {:a 1}) (-> (trie/insert nil "" {:a 1}) (trie/compile) (trie/lookup "")))))