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

View file

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

View file

@ -106,7 +106,7 @@
(defn lookup [^Trie$Matcher matcher 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

View file

@ -568,6 +568,7 @@
;; 662ns (prefix-tree-router)
;; 567ns (segment-router)
;; 326ns (java-segment-router)
;; 194ms (trie)
(b! "reitit" reitit-f)
;; 2845ns
@ -578,10 +579,12 @@
;; 806ns (decode path-parameters)
;; 735ns (maybe-map-values)
;; 474ns (java-segment-router)
#_(b! "reitit-ring" reitit-ring-f)
;; 373ms (trie)
(b! "reitit-ring" reitit-ring-f)
;; 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)
;; 630ns (segment-router-backed)
@ -611,3 +614,19 @@
(comment
(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"]
[funcool/promesa "1.9.0"]
[com.clojure-goes-fast/clj-async-profiler "0.2.2"]
;; https://github.com/bensu/doo/issues/180
[fipp "0.6.14" :exclusions [org.clojure/core.rrb-vector]]]}
:1.9 {:dependencies [[org.clojure/clojure "1.9.0"]]}