char-array is faster, re-run tests

This commit is contained in:
Tommi Reiman 2019-02-02 20:13:45 +02:00
parent 86cfacb03c
commit c87bc099b0
5 changed files with 22 additions and 25 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View file

@ -13,8 +13,8 @@ import java.util.*;
public class Trie { public class Trie {
private static String decode(String ss, int begin, int end, boolean hasPercent, boolean hasPlus) { private static String decode(char[] chars, int begin, int end, boolean hasPercent, boolean hasPlus) {
final String s = ss.substring(begin, end); final String s = new String(chars, begin, end - begin);
try { try {
if (hasPercent) { if (hasPercent) {
return URLDecoder.decode(hasPlus ? s.replace("+", "%2B") : s, "UTF-8"); return URLDecoder.decode(hasPlus ? s.replace("+", "%2B") : s, "UTF-8");
@ -24,18 +24,18 @@ public class Trie {
return s; return s;
} }
private static String decode(String s, int begin, int end) { private static String decode(char[] chars, int begin, int end) {
boolean hasPercent = false; boolean hasPercent = false;
boolean hasPlus = false; boolean hasPlus = false;
for (int j = begin; j < end; j++) { for (int j = begin; j < end; j++) {
final char c = s.charAt(j); final char c = chars[j];
if (c == '%') { if (c == '%') {
hasPercent = true; hasPercent = true;
} else if (c == '+') { } else if (c == '+') {
hasPlus = true; hasPlus = true;
} }
} }
return decode(s, begin, end, hasPercent, hasPlus); return decode(chars, begin, end, hasPercent, hasPlus);
} }
public static class Match { public static class Match {
@ -56,7 +56,7 @@ public class Trie {
} }
public interface Matcher { public interface Matcher {
Match match(int i, int max, String path, Match match); Match match(int i, int max, char[] path, Match match);
int depth(); int depth();
} }
@ -77,12 +77,12 @@ public class Trie {
} }
@Override @Override
public Match match(int i, int max, String path, Match match) { public Match match(int i, int max, char[] path, Match match) {
if (max < i + size) { if (max < i + size) {
return null; return null;
} }
for (int j = 0; j < size; j++) { for (int j = 0; j < size; j++) {
if (path.charAt(j + i) != this.path[j]) { if (path[j + i] != this.path[j]) {
return null; return null;
} }
} }
@ -112,7 +112,7 @@ public class Trie {
} }
@Override @Override
public Match match(int i, int max, String path, Match match) { public Match match(int i, int max, char[] path, Match match) {
if (i == max) { if (i == max) {
match.data = data; match.data = data;
return match; return match;
@ -145,12 +145,12 @@ public class Trie {
} }
@Override @Override
public Match match(int i, int max, String path, Match match) { public Match match(int i, int max, char[] path, Match match) {
if (i < max && path.charAt(i) != '/') { if (i < max && path[i] != '/') {
boolean hasPercent = false; boolean hasPercent = false;
boolean hasPlus = false; boolean hasPlus = false;
for (int j = i; j < max; j++) { for (int j = i; j < max; j++) {
final char c = path.charAt(j); final char c = path[j];
if (c == '/') { if (c == '/') {
final Match m = child.match(j, max, path, match); final Match m = child.match(j, max, path, match);
if (m != null) { if (m != null) {
@ -197,7 +197,7 @@ public class Trie {
} }
@Override @Override
public Match match(int i, int max, String path, Match match) { public Match match(int i, int max, char[] path, Match match) {
if (i < max) { if (i < max) {
match.params.assoc(parameter, decode(path, i, max)); match.params.assoc(parameter, decode(path, i, max));
match.data = data; match.data = data;
@ -233,7 +233,7 @@ public class Trie {
} }
@Override @Override
public Match match(int i, int max, String path, Match match) { public Match match(int i, int max, char[] path, Match match) {
for (int j = 0; j < size; j++) { 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, match);
if (m != null) { if (m != null) {
@ -255,7 +255,7 @@ public class Trie {
} }
public static Object lookup(Matcher matcher, String path) { public static Object lookup(Matcher matcher, String path) {
return matcher.match(0, path.length(), path, new Match()); return matcher.match(0, path.length(), path.toCharArray(), new Match());
} }
public static Matcher scanner(List<Matcher> matchers) { public static Matcher scanner(List<Matcher> matchers) {
@ -263,13 +263,6 @@ public class Trie {
} }
public static void main(String[] args) { public static void main(String[] args) {
//Matcher matcher = new StaticMatcher("/kikka", new StaticMatcher("/kukka", new DataMatcher(1)));
// Matcher matcher =
// staticMatcher("/kikka/",
// wildMatcher(Keyword.intern("kukka"),
// staticMatcher("/kikka",
// dataMatcher(1))));
Matcher matcher = Matcher matcher =
linearMatcher( linearMatcher(
Arrays.asList( Arrays.asList(

View file

@ -20,8 +20,7 @@
;; ;;
(defn h [path] (defn h [path]
(fn [_] (constantly {:status 200, :body path}))
{:status 200, :body path}))
(defn add [handler routes route] (defn add [handler routes route]
(let [method (-> route keys first str/lower-case keyword) (let [method (-> route keys first str/lower-case keyword)
@ -318,6 +317,7 @@
;; 140µs (java-segment-router) ;; 140µs (java-segment-router)
;; 60ns (java-segment-router, no injects) ;; 60ns (java-segment-router, no injects)
;; 55ns (trie-router, no injects) ;; 55ns (trie-router, no injects)
;; 54µs (trie-router, no injects, optimized)
(let [req (map->Req {:request-method :get, :uri "/user/repos"})] (let [req (map->Req {:request-method :get, :uri "/user/repos"})]
(title "static") (title "static")
(assert (= {:status 200, :body "/user/repos"} (app req))) (assert (= {:status 200, :body "/user/repos"} (app req)))
@ -330,6 +330,7 @@
;; 490ns (java-segment-router, no injects) ;; 490ns (java-segment-router, no injects)
;; 440ns (java-segment-router, no injects, single-wild-optimization) ;; 440ns (java-segment-router, no injects, single-wild-optimization)
;; 305ns (trie-router, no injects) ;; 305ns (trie-router, no injects)
;; 281µs (trie-router, no injects, optimized)
(let [req (map->Req {:request-method :get, :uri "/repos/julienschmidt/httprouter/stargazers"})] (let [req (map->Req {:request-method :get, :uri "/repos/julienschmidt/httprouter/stargazers"})]
(title "param") (title "param")
(assert (= {:status 200, :body "/repos/:owner/:repo/stargazers"} (app req))) (assert (= {:status 200, :body "/repos/:owner/:repo/stargazers"} (app req)))
@ -342,6 +343,7 @@
;; 100µs (java-segment-router, no injects) ;; 100µs (java-segment-router, no injects)
;; 90µs (java-segment-router, no injects, single-wild-optimization) ;; 90µs (java-segment-router, no injects, single-wild-optimization)
;; 66µs (trie-router, no injects) ;; 66µs (trie-router, no injects)
;; 64µs (trie-router, no injects, optimized)
(let [requests (mapv route->req routes)] (let [requests (mapv route->req routes)]
(title "all") (title "all")
(cc/quick-bench (cc/quick-bench

View file

@ -69,7 +69,7 @@
;; 25310 / 25126 ;; 25310 / 25126
"regex" "regex"
;; 112017 / 113811 ;; 112719 / 113959
(title "reitit") (title "reitit")
;; wrk -d ${DURATION:="30s"} http://127.0.0.1:2048/product/foo ;; wrk -d ${DURATION:="30s"} http://127.0.0.1:2048/product/foo
;; wrk -d ${DURATION:="30s"} http://127.0.0.1:2048/twenty/bar ;; wrk -d ${DURATION:="30s"} http://127.0.0.1:2048/twenty/bar

View file

@ -584,12 +584,14 @@
;; 474ns (java-segment-router) ;; 474ns (java-segment-router)
;; 373ns (trie) ;; 373ns (trie)
;; 323ns (trie, prioritized) ;; 323ns (trie, prioritized)
;; 289ns (trie, prioritized, zero-copy)
(b! "reitit-ring" reitit-ring-f) (b! "reitit-ring" reitit-ring-f)
;; 385ns (java-segment-router, no injects) ;; 385ns (java-segment-router, no injects)
;; 271ms (trie) ;; 271ms (trie)
;; 240ns (trie, prioritized) ;; 240ns (trie, prioritized)
(b! "reitit-ring-fast" reitit-ring-fast-f) (b! "reitit-ring-fast" reitit-ring-fast-f)
;; 240ns (trie, prioritized, zero-copy)
;; 2553ns (linear-router) ;; 2553ns (linear-router)
;; 630ns (segment-router-backed) ;; 630ns (segment-router-backed)