Better perf with transient parameters

This commit is contained in:
Tommi Reiman 2019-01-27 18:57:29 +02:00
parent f2d131a897
commit fe0ea19e31
5 changed files with 39 additions and 13 deletions

View file

@ -2,7 +2,10 @@ package reitit;
// https://www.codeproject.com/Tips/1190293/Iteration-Over-Java-Collections-with-High-Performa // https://www.codeproject.com/Tips/1190293/Iteration-Over-Java-Collections-with-High-Performa
import clojure.lang.IPersistentMap;
import clojure.lang.ITransientMap;
import clojure.lang.Keyword; import clojure.lang.Keyword;
import clojure.lang.PersistentArrayMap;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
@ -22,14 +25,18 @@ public class Trie {
} }
public static class Match { public static class Match {
public final List<Object> params = new ArrayList<>(); final ITransientMap params = PersistentArrayMap.EMPTY.asTransient();
public Object data; public Object data;
public IPersistentMap parameters() {
return params.persistent();
}
@Override @Override
public String toString() { public String toString() {
Map<Object, Object> m = new HashMap<>(); Map<Object, Object> m = new HashMap<>();
m.put(Keyword.intern("data"), data); m.put(Keyword.intern("data"), data);
m.put(Keyword.intern("params"), params); m.put(Keyword.intern("params"), params.persistent());
return m.toString(); return m.toString();
} }
} }
@ -132,8 +139,7 @@ public class Trie {
if (value[j] == '/') { if (value[j] == '/') {
final Match m = child.match(j, path, match); final Match m = child.match(j, path, match);
if (m != null) { if (m != null) {
m.params.add(key); m.params.assoc(key, decode(value, i, j - i, hasPercent, hasPlus));
m.params.add(decode(value, i, j - i, hasPercent, hasPlus));
} }
return m; return m;
} else if (value[j] == '%') { } else if (value[j] == '%') {
@ -142,12 +148,11 @@ public class Trie {
hasPlus = true; hasPlus = true;
} }
} }
if (child instanceof DataMatcher) { final Match m = child.match(path.size, path, match);
final Match m = child.match(path.size, path, match); if (m != null) {
m.params.add(key); m.params.assoc(key, decode(value, i, path.size - i, hasPercent, hasPlus));
m.params.add(decode(value, i, path.size - i, hasPercent, hasPlus));
return m;
} }
return m;
} }
return null; return null;
} }

View file

@ -2,7 +2,7 @@
(:require [meta-merge.core :refer [meta-merge]] (:require [meta-merge.core :refer [meta-merge]]
[clojure.string :as str] [clojure.string :as str]
[reitit.segment :as segment] [reitit.segment :as segment]
[reitit.segment :as trie] [reitit.trie :as trie]
[reitit.impl :as impl #?@(:cljs [:refer [Route]])]) [reitit.impl :as impl #?@(:cljs [:refer [Route]])])
#?(:clj #?(:clj
(:import (reitit.impl Route)))) (:import (reitit.impl Route))))

View file

@ -106,7 +106,7 @@
(defn lookup [^Trie$Matcher matcher path] (defn lookup [^Trie$Matcher matcher path]
(if-let [match ^Trie$Match (Trie/lookup matcher ^String path)] (if-let [match ^Trie$Match (Trie/lookup matcher ^String path)]
(->Match (.data match) (clojure.lang.PersistentHashMap/create (.toArray (.params match)))))) (->Match (.data match) (.parameters match))))
;; ;;
;; matcher ;; matcher

View file

@ -568,6 +568,7 @@
;; 662ns (prefix-tree-router) ;; 662ns (prefix-tree-router)
;; 567ns (segment-router) ;; 567ns (segment-router)
;; 326ns (java-segment-router) ;; 326ns (java-segment-router)
;; 194ms (trie)
(b! "reitit" reitit-f) (b! "reitit" reitit-f)
;; 2845ns ;; 2845ns
@ -578,10 +579,12 @@
;; 806ns (decode path-parameters) ;; 806ns (decode path-parameters)
;; 735ns (maybe-map-values) ;; 735ns (maybe-map-values)
;; 474ns (java-segment-router) ;; 474ns (java-segment-router)
#_(b! "reitit-ring" reitit-ring-f) ;; 373ms (trie)
(b! "reitit-ring" reitit-ring-f)
;; 385ns (java-segment-router, no injects) ;; 385ns (java-segment-router, no injects)
#_(b! "reitit-ring-fast" reitit-ring-fast-f) ;; 271ms (trie)
(b! "reitit-ring-fast" reitit-ring-fast-f)
;; 2553ns (linear-router) ;; 2553ns (linear-router)
;; 630ns (segment-router-backed) ;; 630ns (segment-router-backed)
@ -611,3 +614,19 @@
(comment (comment
(bench-rest!)) (bench-rest!))
(set! *warn-on-reflection* true)
(require '[clj-async-profiler.core :as prof])
(comment
;; 629ms (arraylist)
;; 395ns (transient)
(let [app (ring/ring-handler (ring/router opensensors-routes))]
(doseq [[p r] (-> app (ring/get-router) (r/routes))]
(when-not (app {:uri p, :request-method :get})
(println "FAIL:" p)))
(println (app {:uri "/v1/users/1/devices/1", :request-method :get}))
(prof/start {})
(dotimes [_ 100000]
(app {:uri "/v1/users/1/devices/1", :request-method :get}))
(str (prof/stop {}))))

View file

@ -91,6 +91,8 @@
[manifold "0.1.8"] [manifold "0.1.8"]
[funcool/promesa "1.9.0"] [funcool/promesa "1.9.0"]
[com.clojure-goes-fast/clj-async-profiler "0.2.2"]
;; https://github.com/bensu/doo/issues/180 ;; https://github.com/bensu/doo/issues/180
[fipp "0.6.14" :exclusions [org.clojure/core.rrb-vector]]]} [fipp "0.6.14" :exclusions [org.clojure/core.rrb-vector]]]}
:1.9 {:dependencies [[org.clojure/clojure "1.9.0"]]} :1.9 {:dependencies [[org.clojure/clojure "1.9.0"]]}