From 4c722d628ee48a1dbd3c0ca89041ea541d2797ab Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 14 Jan 2019 17:27:16 +0200 Subject: [PATCH] Calfpath-data --- doc/images/opensensors.png | Bin 29958 -> 37115 bytes doc/performance.md | 13 +-- .../clj/reitit/opensensors_perf_test.clj | 75 +++++++++++++++++- 3 files changed, 78 insertions(+), 10 deletions(-) diff --git a/doc/images/opensensors.png b/doc/images/opensensors.png index d59d6dab965bb85c9b746ac394deeafd36b481de..94bf228a70ee06c67836a722884217eefdaa3cbb 100644 GIT binary patch literal 37115 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 literal 29958 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
V?Mx8zW~%5Mu{mf0;vIf~-E|@iljFaPYz36F$R6 z2NAk1G6IfEGgj$STo`R-vo>kJ;m(-INvZ9hHFLllSxC<_2F^BuM|1-w_dZlNAt9mo zdOMpJWT70O44K<}#oEC0U}jr{U@gD<{kzqhDSSLQeB=d)L#O6 zXCW(9fTUdzp7+_g8-fa$u}=+a4tiXb+69L)hgIh?ViwCCOC|yXlm z2PlDIF-IInk6l`V8;Ol4!Xv8K_#XexD_zoscOfkgejh|qeEryWgKbm|oVyl^&(~G? z7_&TiiZb;Y#g|d54tkr^{WD22uf+RB-GW*yt!Dr(zqnqpIh|Py-Rzg=vU6}C>z&~aC*0GDr0e2g9%Ege*^7&dfA8wQ;1aX4 zxZpnWSUn>E6gXN1_boSNi2hdQ`Z(KpkB}VNmb#I$F4KFLSKTbWI&+uH%P+S&%w3<) z3sO3&)3`@}$Re>)HfrR}fegRqr|g1UXaFN(1}`czSX2uhFjLH#UUFQ~a? z58RCbOss-|^oek1n~TMFZOYV!q#8NB#x8mT%R^n~V`{AWrCL_Z^g}=2ORjExvtc~h zsW-K!_Z;ctl=?2TN;~B8ncDZ*_VjeeHZF9C*cx6A?k6g+o{+Krv=ncDX*t(f{scxR zKy#ZBL?m8H1zo#U5w6V9}GHYCZQ1K3bZ z0UB9=Yz&f5xP$~G8%i)1RKTcfz=m4vVLrZ}8BPYK)(M zT-9+DYCJeNe{Wu3gXo|CF43eD=c1OVj7-Zf7bP84!-U`ZI}ku*a$N4mMqzl-szJlbU+c{xQ^YoaxOiiDXv}lB5+dVbmk_ z+8xtFU*rh`v|%-hXp->h2UQ|&S51%GBAN6<8X2jt4A*TMg%adq6^j+gW%;b^>>M7t zWQ2xi#h9r-?_XT~Sl4qT`<o?Vm+ty8%b1Yr-Km^ z721!vHAZt6LxgN#{IMxB-f9mp!1o4gOCM{1n+9-7r#BPBUb9F&z>UIF`fM7*HAsO< z07=T;n<2?9pUz-m8?WT2Stg*_oA8h~T2`dp>*eHAz1T25^O51(w{gm?1)&9>?orlW zd}H18%Cb=z^USyU1)p4~I=49SsF{_$>RRS4=)Ep0Cp8g1wgB__@W_l`Sk-0WNJ zo42eOUhnC=HK_0uJAbos?se1Ptd5wK^KrwsYTgXpw2LlNn}sHhtU{lhA}gl7IPQv= z>CMi`O&9Ry8$NIKpc(x!kq|E5kmg)CVKi{UnESqq0K>rj$1@*kr`vp6o0h!1lSbQa zseGIZ^t?8%t67Q79dWp%zJP5VU2s%R(yOR=Mt#wiSHseI z=C(wylIBx)=GB?t>STA+__-cp9s>8CE7k{}b423TbGn1nPbu-}EJsYVv50L$; z_sPPCc_FYew&rP|RznVE!74GB1xF=m{RgIj#z!m>=x$*2xwf-#{)N^S|E{+w+mQuM zdcd1QG-b1e&8o$l6p~^2>SkI8liNy_`T4gQ`*EGpck)eY>pS227XP(>QRsL|Iv0XT zbsDPcxUu3A<&{P))i%pq6m8&1NA2lVHomw{lp@v-|efw4Dqo;;~=E1b>kk=-9w2WJRkBV;g7)I5>l^zRj0EBNa{hHi;w!$j{&3sT~FPA{aWSIQjxiE$OrJH_34xTwI-p z;CVPaKTiFPMQaW7bDgx78#VqsS&adPcosVIzO-KjR}cf~KJx#=efj0f{x|3R@)@`S z+{am(18`)ByY%x*hbRz>3Gmu8mM^C`A>+XZt=dMuWQLSk-!J!%saPi{h!wiW`nldA zL4myJ_|aFWb82cIy1}(DjkqM?Cq@4X(q9en6s|rIghDLoifA>0-f!t0od0oq{cvQI zW+bJD2xIu=SlFT2W5>9f!B6>ZhIg}p%}sND+8?KWq@icb0?c`ti}}}XyYbGYHbDkU z3n`3D$JD{1Rf5SEJ@b^$uE1SePU2xo(5L@vq=Dp0chg-Nn9p@E_=97`vyo2oPpaoz zIw)xnARXJrL5uj{$1VTPK^@)|w>(GsXHeL58xTw!vx(#%=spS~2FCp7XyVgYKR-*? zD~iQInZDO0 ztTSV>h=Z8W$}UQu_}A@$oFgi$;&iGq zHXTjCv6X>(J#uPa-&PMNkb-`I|1_qs@5b!w0XQlI`ql?DZnJ7p`t#>(oVri-6p^?E zI6Z$YKdQgKz@4iuQj8@+5hl+2h+fP(9{2qNwdrQSt@n!b zQeHw*UNjVqeHXbgwthHgvSojM9E%P{Z)b(v@aE1P!^=(S4L>w4bjyn`?+kZCVP+<= z@efrc?Qdq8nUuvSGc-}g#y)s}WD!LJfIwg9l+d|kG^7K=r0>k}T<&8qm3TieFK+jY z#wK%xy6cRJB14^0{&LJ@(KcVaZ@XpW^Xw_gR{>q~U(;#GBpADZ1&GBW1+ob+K@m7> z@^jbrT9 zA8hLmkvg+#$%8(Liac>S2;mYM015o>I*2UGPn4BysUQtn6agXV)SS2ex34|;d`Fn= zHzo&mcRB?3s^jQ2Bo9D!w(266l`}t<=^==Rc$?mC(Xd~%&#`3ocCZ@h+(Mfl!c0fN z4@kTVA#wfYzW>LSI=iB4-zhgL69ri4&cSxOBk(D#kmbwWN0OA3a%2 zzVlg&`i{*rUX$FIQ;1H79#2Jdv@$WVQu(AV#1vt^AQWoYl9@zN87ytb{T-)*#ZzT9 zR{lv$NJ96&E;MOj+9dW@g*SLks(AeR01lLh(=e4#GP>{o?wbF;V@dhtsvodoNnz4} z zCbU1`YAnQu$XV^<=o!(*GSjN@rEQJ3XKB(y)9Oo9dtaaPp4l4WuCSCbO=tIXHWd3f zL4BkBafKaq{8msrFV42u%3P!X{QkYR6Zdr7F%iUkn@a z8bY6@9gL5xC_nL~>hN^_k_#*rogPfwyjF8`oH8QfpOBf*;*#N#54)Q`wHeyGW{P-9 zhHW25bv9w zr1zRv6nHACq^f07e6;+M!HcT=Vp?&7naLxo{V5zSI%>}oxao(>PaibBF+?iL6o{J! zdFhCspcBEO8N>^N56=$^7u6QV7q?M!cSJ_^&c_z>8@|EwO?C#|80RAqbhs7>u||kq z--iWBRC;r0>y$w9`^{DU!M4a2a;2zBt4NkLwE_O2lienActwl=BY(+XrA$Tb3pji*Qb7FUO$gtQx!p)jz!kQqjI z=KX%9EX&8nFO+Y(jvRk<1OfZ}jz=|Uvgka-ZO2t1)!_Jn^R!>WW2AD!NzA&1ajQMt z*8&Qgkc$(pdT+c@;{_4eVe+TXkJ48gNKs8uVDjZzbkJ?D&jRjwzuXy+RIdVfvk)md zj%2wZ55CB2KNEzbsoX3uyS6~Zu_ePbT=i9T=$@S_^>fGTIbR09ep7^hp#Zl+WO0i` zzjtS!Y_fIlTrWx^PWmGx=yha-x}v$!o5NjpQ372@mtRbnbXUZ3u_=^ySDb&<$735y z*-2y3iDw@f3R~_UbhvDBtE159T;sZuTP^M9)Mm+M2oE=&h&WHshm~2iUB3?4dw>e^;@DciO(anEZcHYVOUEQJn0m+Z_21-h;{Ev3G z%O!QpEZ^V5)6CdX_gLB4Pkp!Be0lu=+Z)OV*U5pVD8w;x;y6Yt+f&&~)tY3Vrdi2# zvE-h%k;~Zz$Ns^A&$jGRZ1K9!dtXU^)Jp&1?V;PVeJUwU8v8SVoqkJh3e8wC+enF208zPJU}iV@6QfjnGKv=64)SkX_~%~vokY0OX@+( zt8WI;t{kZ5Z%MWAL?JBurQy{ac-nYH{cs6KbEv~|T^=JB4hh1(<*!B0{7?!Hc8B7Pv!WH)FWJQK0 zQ|^%XPTvbS>lCQ7X>ozERDYJi*wZ*HS7cydawI_3y$wGk@o7N3c$9kfuyBDgUwiml zgn77+w?IN!P*%F1+}!;mMu;1Aa{1fUTw%$c!W%kKMtnX2n#OAI4;lcP_L=&x9= z)AkA6wQD*_tsx=?Ij#8O29t*xBlre&#yzzQnH5ZPOg;U~_q#%$k=u+t5sPs_h2 z!cLAUh5h3mwk>u?C9IO?h~J7c2Q=^lr~w>D+}jmt`+;b02mdle0+ZM5>*87(!!f2a zwEi}GfeF3tY`^mf0cyCBsE{hTplA8h3~U#pS3eeeTVYgayw#4G!ue%L-= z+?a~S_-%{8le2Fn{?K59;64vN!0#I!(mHULS>5UrguCT;m(iMV pnV*$X{r779|L^=y`Fz 1.9M ops/sec +;; Execution time mean (per 1000): 315 µs -> 3.2M ops/sec (cc/quick-bench (dotimes [_ 1000] (r/match-by-path routes "/workspace/1/1"))) ``` -Based on the [perf tests](https://github.com/metosin/reitit/tree/master/perf-test/clj/reitit/perf/bide_perf_test.clj), the first (static path) lookup is 300-500x faster and the second (wildcard path) lookup is 4-24x faster that the other tested routing libs (Ataraxy, Bidi, Compojure and Pedestal). +Based on the [perf tests](https://github.com/metosin/reitit/tree/master/perf-test/clj/reitit/perf/bide_perf_test.clj), the first (static path) lookup is 300-500x faster and the second (wildcard path) lookup is 6-40x faster that the other tested routing libs (Ataraxy, Bidi, Compojure and Pedestal). But, the example is too simple for any real benchmark. Also, some of the libraries always match on the `:request-method` too and by doing so, do more work than just match by path. Compojure does most work also by invoking the handler. @@ -79,7 +79,7 @@ So, we need to test something more realistic. To get better view on the real life routing performance, there is [test](https://github.com/metosin/reitit/blob/master/perf-test/clj/reitit/opensensors_perf_test.clj) of a mid-size rest(ish) http api with 50+ routes, having a lot of path parameters. The route definitions are pulled off from the [OpenSensors](https://opensensors.io/) swagger definitions. -Thanks to the snappy new [segment-tree](https://github.com/metosin/reitit/blob/master/modules/reitit-core/src/reitit/segment.cljc) algorithm, `reitit-ring` is fastest here. Pedestal is also fast with it's [prefix-tree](https://en.wikipedia.org/wiki/Radix_tree) implementation. +Thanks to the snappy [SegmentTrie](https://github.com/metosin/reitit/blob/master/modules/reitit-core/java-src/reitit/SegmentTrie.java) (a modification of [Radix tree](https://en.wikipedia.org/wiki/Radix_tree)), `reitit-ring` is fastest here. [Calfpath](https://github.com/kumarshantanu/calfpath) and [Pedestal](https://github.com/pedestal/pedestal) are also quite fast. ![Opensensors perf](images/opensensors.png) @@ -99,13 +99,14 @@ The reitit routing perf is measured to get an internal baseline to optimize agai ### Looking out of the box -A quick poke to [routers in Go](https://github.com/julienschmidt/go-http-routing-benchmark) indicates that the reitit is only few times slower than the fastest routers in Go. Which is really awesome (if true). +A quick poke to [routers in Go](https://github.com/julienschmidt/go-http-routing-benchmark) indicates that the reitit is only few times slower than the fastest routers in Go. Which is kinda awesome. ### Performance tips Few things that have an effect on performance: * Wildcard-routes are an order of magnitude slower than static routes -* It's ok to mix non-wildcard and wildcard routes in a same routing tree as long as you don't disable the [conflict resolution](basics/route_conflicts.md) => if no conflicting routes are found, a `:mixed-router` can be created, which internally has a fast static path router and a separate wildcard-router. So, the static paths are still fast. +* Conflicting routes are served with LinearRouter, which is the slowest implementation. +* It's ok to mix non-wildcard, wildcard or even conflicting routes in a same routing tree. Reitit will create an hierarchy of routers to serve all the routes with best possible implementation. * Move computation from request processing time into creation time, using by compiling [middleware](ring/compiling_middleware.md) & [route data](advanced/configuring_routers.md). * Unmounted middleware (or interceptor) is infinitely faster than a mounted one effectively doing nothing. diff --git a/perf-test/clj/reitit/opensensors_perf_test.clj b/perf-test/clj/reitit/opensensors_perf_test.clj index eb6bc094..ba8c3813 100644 --- a/perf-test/clj/reitit/opensensors_perf_test.clj +++ b/perf-test/clj/reitit/opensensors_perf_test.clj @@ -9,6 +9,7 @@ [ataraxy.core :as ataraxy] [compojure.core :refer [routes context ANY]] [calfpath.core :as cp] + [calfpath.route :as cr] [io.pedestal.http.route.definition.table :as table] [io.pedestal.http.route.map-tree :as map-tree] @@ -367,7 +368,7 @@ ["/v1/users/:user-id/bookmarks" :get handler :route-name :test/route56] ["/v1/orgs/:org-id/topics" :get handler :route-name :test/route57]]))) -(defn opensensors-calfpath-handler [request] +(defn opensensors-calfpath-macro-handler [request] (cp/->uri request "/v2/whoami" [] (cp/->get request (handler request) nil) @@ -429,6 +430,68 @@ "/v1/orgs/:org-id/topics" [] (cp/->get request (handler request) nil) nil)) +(def opensensors-calfpath-routes + [{:uri "/v2/whoami" :nested [{:method :get, :handler handler}]} + {:uri "/v2/users/:user-id/datasets" :nested [{:method :get, :handler handler}]} + {:uri "/v2/public/projects/:project-id/datasets" :nested [{:method :get, :handler handler}]} + {:uri "/v1/public/topics/:topic" :nested [{:method :get, :handler handler}]} + {:uri "/v1/users/:user-id/orgs/:org-id" :nested [{:method :get, :handler handler}]} + {:uri "/v1/search/topics/:term" :nested [{:method :get, :handler handler}]} + {:uri "/v1/users/:user-id/invitations" :nested [{:method :get, :handler handler}]} + {:uri "/v1/orgs/:org-id/devices/:batch/:type" :nested [{:method :get, :handler handler}]} + {:uri "/v1/users/:user-id/topics" :nested [{:method :get, :handler handler}]} + {:uri "/v1/users/:user-id/bookmarks/followers" :nested [{:method :get, :handler handler}]} + {:uri "/v2/datasets/:dataset-id" :nested [{:method :get, :handler handler}]} + {:uri "/v1/orgs/:org-id/usage-stats" :nested [{:method :get, :handler handler}]} + {:uri "/v1/orgs/:org-id/devices/:client-id" :nested [{:method :get, :handler handler}]} + {:uri "/v1/messages/user/:user-id" :nested [{:method :get, :handler handler}]} + {:uri "/v1/users/:user-id/devices" :nested [{:method :get, :handler handler}]} + {:uri "/v1/public/users/:user-id" :nested [{:method :get, :handler handler}]} + {:uri "/v1/orgs/:org-id/errors" :nested [{:method :get, :handler handler}]} + {:uri "/v1/public/orgs/:org-id" :nested [{:method :get, :handler handler}]} + {:uri "/v1/orgs/:org-id/invitations" :nested [{:method :get, :handler handler}]} + {:uri "/v2/public/messages/dataset/bulk" :nested [{:method :get, :handler handler}]} + {:uri "/v1/users/:user-id/devices/bulk" :nested [{:method :get, :handler handler}]} + {:uri "/v1/users/:user-id/device-errors" :nested [{:method :get, :handler handler}]} + {:uri "/v2/login" :nested [{:method :get, :handler handler}]} + {:uri "/v1/users/:user-id/usage-stats" :nested [{:method :get, :handler handler}]} + {:uri "/v2/users/:user-id/devices" :nested [{:method :get, :handler handler}]} + {:uri "/v1/users/:user-id/claim-device/:client-id" :nested [{:method :get, :handler handler}]} + {:uri "/v2/public/projects/:project-id" :nested [{:method :get, :handler handler}]} + {:uri "/v2/public/datasets/:dataset-id" :nested [{:method :get, :handler handler}]} + {:uri "/v2/users/:user-id/topics/bulk" :nested [{:method :get, :handler handler}]} + {:uri "/v1/messages/device/:client-id" :nested [{:method :get, :handler handler}]} + {:uri "/v1/users/:user-id/owned-orgs" :nested [{:method :get, :handler handler}]} + {:uri "/v1/topics/:topic" :nested [{:method :get, :handler handler}]} + {:uri "/v1/users/:user-id/bookmark/:topic" :nested [{:method :get, :handler handler}]} + {:uri "/v1/orgs/:org-id/members/:user-id" :nested [{:method :get, :handler handler}]} + {:uri "/v1/users/:user-id/devices/:client-id" :nested [{:method :get, :handler handler}]} + {:uri "/v1/users/:user-id" :nested [{:method :get, :handler handler}]} + {:uri "/v1/orgs/:org-id/devices" :nested [{:method :get, :handler handler}]} + {:uri "/v1/orgs/:org-id/members" :nested [{:method :get, :handler handler}]} + {:uri "/v1/orgs/:org-id/members/invitation-data/:user-id" :nested [{:method :get, :handler handler}]} + {:uri "/v2/orgs/:org-id/topics" :nested [{:method :get, :handler handler}]} + {:uri "/v1/whoami" :nested [{:method :get, :handler handler}]} + {:uri "/v1/orgs/:org-id" :nested [{:method :get, :handler handler}]} + {:uri "/v1/users/:user-id/api-key" :nested [{:method :get, :handler handler}]} + {:uri "/v2/schemas" :nested [{:method :get, :handler handler}]} + {:uri "/v2/users/:user-id/topics" :nested [{:method :get, :handler handler}]} + {:uri "/v1/orgs/:org-id/confirm-membership/:token" :nested [{:method :get, :handler handler}]} + {:uri "/v2/topics/:topic" :nested [{:method :get, :handler handler}]} + {:uri "/v1/messages/topic/:topic" :nested [{:method :get, :handler handler}]} + {:uri "/v1/users/:user-id/devices/:client-id/reset-password" :nested [{:method :get, :handler handler}]} + {:uri "/v2/topics" :nested [{:method :get, :handler handler}]} + {:uri "/v1/login" :nested [{:method :get, :handler handler}]} + {:uri "/v1/users/:user-id/orgs" :nested [{:method :get, :handler handler}]} + {:uri "/v2/public/messages/dataset/:dataset-id" :nested [{:method :get, :handler handler}]} + {:uri "/v1/topics" :nested [{:method :get, :handler handler}]} + {:uri "/v1/orgs" :nested [{:method :get, :handler handler}]} + {:uri "/v1/users/:user-id/bookmarks" :nested [{:method :get, :handler handler}]} + {:uri "/v1/orgs/:org-id/topics" :nested [{:method :get, :handler handler}]}]) + +(def opensensors-calfpath-data-handler + (partial cr/dispatch (cr/compile-routes opensensors-calfpath-routes {:show-uris-400? false}))) + (comment (pedestal/find-route (map-tree/router @@ -487,7 +550,8 @@ reitit-ring-f (ring/ring-handler (ring/router opensensors-routes)) reitit-ring-fast-f (ring/ring-handler (ring/router opensensors-routes) nil {:inject-router? false, :inject-match? false}) bidi-f #(bidi/match-route opensensors-bidi-routes (:uri %)) - calfpath-f opensensors-calfpath-handler + calfpath-macros-f opensensors-calfpath-macro-handler + calfpath-data-f opensensors-calfpath-data-handler ataraxy-f (partial ataraxy/matches opensensors-ataraxy-routes) compojure-f opensensors-compojure-routes pedestal-f (partial pedestal/find-route opensensors-pedestal-routes) @@ -513,11 +577,14 @@ ;; 385ns (java-segment-router, no injects) (b! "reitit-ring-fast" reitit-ring-fast-f) + ;; 2258ns + (b! "calfpath-data" calfpath-data-f) + ;; 2821ns (b! "pedestal" pedestal-f) - ;; 4364ns (macros) - (b! "calfpath" calfpath-f) + ;; 4364ns + (b! "calfpath-macros" calfpath-macros-f) ;; 11615ns (b! "compojure" compojure-f)