mirror of
https://github.com/metosin/reitit.git
synced 2025-12-17 08:21:11 +00:00
char-array is faster, re-run tests
This commit is contained in:
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 |
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue