2017-11-01 07:29:38 +00:00
<!DOCTYPE HTML>
< html lang = "" >
< head >
< meta charset = "UTF-8" >
< meta content = "text/html; charset=utf-8" http-equiv = "Content-Type" >
< title > Performance · GitBook< / title >
< meta http-equiv = "X-UA-Compatible" content = "IE=edge" / >
< meta name = "description" content = "" >
< meta name = "generator" content = "GitBook 3.2.3" >
< link rel = "stylesheet" href = "gitbook/style.css" >
< link rel = "stylesheet" href = "gitbook/gitbook-plugin-highlight/website.css" >
< link rel = "stylesheet" href = "gitbook/gitbook-plugin-search/search.css" >
< link rel = "stylesheet" href = "gitbook/gitbook-plugin-fontsettings/website.css" >
< meta name = "HandheldFriendly" content = "true" / >
< meta name = "viewport" content = "width=device-width, initial-scale=1, user-scalable=no" >
< meta name = "apple-mobile-web-app-capable" content = "yes" >
< meta name = "apple-mobile-web-app-status-bar-style" content = "black" >
< link rel = "apple-touch-icon-precomposed" sizes = "152x152" href = "gitbook/images/apple-touch-icon-precomposed-152.png" >
< link rel = "shortcut icon" href = "gitbook/images/favicon.ico" type = "image/x-icon" >
< link rel = "prev" href = "ring/compiling_middleware.html" / >
< / head >
< body >
< div class = "book" >
< div class = "book-summary" >
< div id = "book-search-input" role = "search" >
< input type = "text" placeholder = "Type to search" / >
< / div >
< nav role = "navigation" >
< ul class = "summary" >
< li class = "chapter " data-level = "1.1" data-path = "./" >
< a href = "./" >
Introduction
< / a >
< / li >
< li class = "chapter " data-level = "1.2" data-path = "basics/" >
< a href = "basics/" >
Basics
< / a >
< ul class = "articles" >
< li class = "chapter " data-level = "1.2.1" data-path = "basics/route_syntax.html" >
< a href = "basics/route_syntax.html" >
Route syntax
< / a >
< / li >
< li class = "chapter " data-level = "1.2.2" data-path = "basics/router.html" >
< a href = "basics/router.html" >
Router
< / a >
< / li >
< li class = "chapter " data-level = "1.2.3" data-path = "basics/path_based_routing.html" >
< a href = "basics/path_based_routing.html" >
Path-based Routing
< / a >
< / li >
< li class = "chapter " data-level = "1.2.4" data-path = "basics/name_based_routing.html" >
< a href = "basics/name_based_routing.html" >
Name-based Routing
< / a >
< / li >
< li class = "chapter " data-level = "1.2.5" data-path = "basics/route_data.html" >
< a href = "basics/route_data.html" >
Route data
< / a >
< / li >
< li class = "chapter " data-level = "1.2.6" data-path = "basics/route_conflicts.html" >
< a href = "basics/route_conflicts.html" >
Route conflicts
< / a >
< / li >
< / ul >
< / li >
< li class = "chapter " data-level = "1.3" data-path = "advanced/" >
< a href = "advanced/" >
Advanced
< / a >
< ul class = "articles" >
< li class = "chapter " data-level = "1.3.1" data-path = "advanced/configuring_routers.html" >
< a href = "advanced/configuring_routers.html" >
Configuring routers
< / a >
< / li >
< li class = "chapter " data-level = "1.3.2" data-path = "advanced/different_routers.html" >
< a href = "advanced/different_routers.html" >
Different Routers
< / a >
< / li >
< li class = "chapter " data-level = "1.3.3" data-path = "advanced/route_validation.html" >
< a href = "advanced/route_validation.html" >
Route Validation
< / a >
< / li >
< / ul >
< / li >
< li class = "chapter " data-level = "1.4" data-path = "ring/" >
< a href = "ring/" >
Ring
< / a >
< ul class = "articles" >
< li class = "chapter " data-level = "1.4.1" data-path = "ring/ring.html" >
< a href = "ring/ring.html" >
Ring-router
< / a >
< / li >
< li class = "chapter " data-level = "1.4.2" data-path = "ring/dynamic_extensions.html" >
< a href = "ring/dynamic_extensions.html" >
Dynamic extensions
< / a >
< / li >
< li class = "chapter " data-level = "1.4.3" data-path = "ring/data_driven_middleware.html" >
< a href = "ring/data_driven_middleware.html" >
Data-driven Middleware
< / a >
< / li >
< li class = "chapter " data-level = "1.4.4" data-path = "ring/parameter_coercion.html" >
< a href = "ring/parameter_coercion.html" >
Parameter coercion
< / a >
< / li >
< li class = "chapter " data-level = "1.4.5" data-path = "ring/compiling_middleware.html" >
< a href = "ring/compiling_middleware.html" >
Compiling middleware
< / a >
< / li >
< / ul >
< / li >
< li class = "chapter active" data-level = "1.5" data-path = "performance.html" >
< a href = "performance.html" >
Performance
< / a >
< / li >
< li class = "chapter " data-level = "1.6" >
< span >
TODO: Swagger & OpenAPI
< / span >
< / li >
< li class = "chapter " data-level = "1.7" >
< span >
TODO: Interceptors
< / span >
< / li >
< li class = "divider" > < / li >
< li >
< a href = "https://www.gitbook.com" target = "blank" class = "gitbook-link" >
Published with GitBook
< / a >
< / li >
< / ul >
< / nav >
< / div >
< div class = "book-body" >
< div class = "body-inner" >
< div class = "book-header" role = "navigation" >
<!-- Title -->
< h1 >
< i class = "fa fa-circle-o-notch fa-spin" > < / i >
< a href = "." > Performance< / a >
< / h1 >
< / div >
< div class = "page-wrapper" tabindex = "-1" role = "main" >
< div class = "page-inner" >
< div id = "book-search-results" >
< div class = "search-noresults" >
< section class = "normal markdown-section" >
< h1 id = "performance" > Performance< / h1 >
< p > There are many great routing libraries for Clojure(Script), but not many are optimized for perf. Reitit tries to be both great in features and really fast. Originally the routing was adopted from < a href = "http://pedestal.io/" target = "_blank" > Pedestal< / a > , but it has been since mostly rewritten.< / p >
< h3 id = "rationale" > Rationale< / h3 >
< ul >
< li > Multiple routing algorithms, select for for a given route tree< / li >
< li > Route flattening and re-ordering< / li >
< li > Managed mutability over Immutability< / li >
< li > Precompute/compile as much as possible (matches, middleware, routes)< / li >
< li > Use abstractions that enable JVM optimizations< / li >
< li > Use small functions to enable JVM Inlining< / li >
< li > Protocols over Multimethods< / li >
< li > Records over Maps< / li >
< li > Always be measuring< / li >
< li > Don' t trust the (micro-)benchmarks< / li >
< / ul >
< h3 id = "performance-guides" > Performance guides< / h3 >
< p > As a library user, some things related to performance:< / p >
< ul >
< li > avoid wildcard-routes - it' s an order of magnitude slower to match than non-wildcard routes< / li >
< li > it' s ok to mix non-wildcard and wildcard routes in a same routing tree as long as you don' t disable the < a href = "basics/route_conflicts.html" > conflict resolution< / a > => if no conflicting routes are found, a < code > :mixed-router< / code > can be created, which collects all non-wildcard routes into a separate fast subrouter.< / li >
< / ul >
< h3 id = "example" > Example< / h3 >
< p > The routing sample taken from < a href = "https://github.com/funcool/bide" target = "_blank" > bide< / a > perf suite, run with a Late 2013 MacBook Pro, with the < code > perf< / code > profile:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > require< / span > ' [reitit.core < span class = "hljs-symbol" > :as< / span > r])
(< span class = "hljs-name" > require< / span > ' [criterium.core < span class = "hljs-symbol" > :as< / span > cc])
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > def< / span > < / span > routes
(< span class = "hljs-name" > r/router< / span >
[[< span class = "hljs-string" > " /auth/login" < / span > < span class = "hljs-symbol" > :auth/login< / span > ]
[< span class = "hljs-string" > " /auth/recovery/token/:token" < / span > < span class = "hljs-symbol" > :auth/recovery< / span > ]
[< span class = "hljs-string" > " /workspace/:project/:page" < / span > < span class = "hljs-symbol" > :workspace/page< / span > ]]))
< span class = "hljs-comment" > ;; Execution time mean : 3.488297 µ s -> 286M ops/sec< / span >
(< span class = "hljs-name" > cc/quick-bench< / span >
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > dotimes< / span > < / span > [_ < span class = "hljs-number" > 1000< / span > ]
(< span class = "hljs-name" > r/match-by-path< / span > routes < span class = "hljs-string" > " /auth/login" < / span > )))
< span class = "hljs-comment" > ;; Execution time mean : 692.905995 µ s -> 1.4M ops/sec< / span >
(< span class = "hljs-name" > cc/quick-bench< / span >
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > dotimes< / span > < / span > [_ < span class = "hljs-number" > 1000< / span > ]
(< span class = "hljs-name" > r/match-by-path< / span > routes < span class = "hljs-string" > " /workspace/1/1" < / span > )))
< / code > < / pre >
< h3 id = "is-that-good" > Is that good?< / h3 >
2017-11-01 07:36:59 +00:00
< p > Based on some < a href = "https://github.com/metosin/reitit/tree/master/perf-test/clj/reitit" target = "_blank" > quick tests< / a > , the first lookup is order of 100 times faster than other tested Clojure routing libraries. The second being 3-18x faster. But as like most microbenchmarks, test usually lie as they test different test things with different libs. For example, Pedestal also matches on the < code > :request-method< / code > which makes it do more work. With real life routing trees, the differences are most likely more subtle, or even the order might be totally different.< / p >
2017-11-01 07:29:38 +00:00
< h3 id = "so-why-test" > So why test?< / h3 >
< p > Real value of perf tests is to have a internal baseline and optimize against it. Also, to ensure that new features don' t regress the performance.< / p >
< p > It might be interesting to look out of the box and compare Clojure routing libs to routers in other languages, like the < a href = "https://github.com/julienschmidt/go-http-routing-benchmark" target = "_blank" > routers in Go< / a > .< / p >
< h3 id = "plans-ahead" > Plans ahead< / h3 >
< p > Currently, the non-wildcard routes are already really fast to match, but wildcard routes use only a naive linear scan. Plan is to add a optimized < a href = "https://en.wikipedia.org/wiki/Trie" target = "_blank" > Trie< / a > -based router. See
< a href = "https://github.com/julienschmidt/httprouter#how-does-it-work" target = "_blank" > httprouter< / a > and < a href = "https://github.com/pedestal/pedestal/pull/330" target = "_blank" > Pedestal< / a > for details. New routing algorithms can be plugged in easily, without changes in the public apis.< / p >
< / section >
< / div >
< div class = "search-results" >
< div class = "has-results" >
< h1 class = "search-results-title" > < span class = 'search-results-count' > < / span > results matching "< span class = 'search-query' > < / span > "< / h1 >
< ul class = "search-results-list" > < / ul >
< / div >
< div class = "no-results" >
< h1 class = "search-results-title" > No results matching "< span class = 'search-query' > < / span > "< / h1 >
< / div >
< / div >
< / div >
< / div >
< / div >
< / div >
< a href = "ring/compiling_middleware.html" class = "navigation navigation-prev navigation-unique" aria-label = "Previous page: Compiling middleware" >
< i class = "fa fa-angle-left" > < / i >
< / a >
< / div >
< script >
var gitbook = gitbook || [];
gitbook.push(function() {
2017-11-01 07:36:59 +00:00
gitbook.page.hasChanged({"page":{"title":"Performance","level":"1.5","depth":1,"next":{"title":"TODO: Swagger & OpenAPI","level":"1.6","depth":1,"ref":"","articles":[]},"previous":{"title":"Compiling middleware","level":"1.4.5","depth":2,"path":"ring/compiling_middleware.md","ref":"ring/compiling_middleware.md","articles":[]},"dir":"ltr"},"config":{"plugins":["editlink","github","highlight"],"root":"doc","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"editlink":{"label":"Edit This Page","multilingual":false,"base":"https://github.com/metosin/reitit/tree/master/doc"},"github":{"url":"https://github.com/metosin/reitit"},"highlight":{},"search":{},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"gitbook":"*"},"file":{"path":"performance.md","mtime":"2017-11-01T07:36:17.789Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2017-11-01T07:36:56.657Z"},"basePath":".","book":{"language":""}});
2017-11-01 07:29:38 +00:00
});
< / script >
< / div >
< script src = "gitbook/gitbook.js" > < / script >
< script src = "gitbook/theme.js" > < / script >
< script src = "gitbook/gitbook-plugin-editlink/plugin.js" > < / script >
< script src = "gitbook/gitbook-plugin-github/plugin.js" > < / script >
< script src = "gitbook/gitbook-plugin-search/search-engine.js" > < / script >
< script src = "gitbook/gitbook-plugin-search/search.js" > < / script >
< script src = "gitbook/gitbook-plugin-lunr/lunr.min.js" > < / script >
< script src = "gitbook/gitbook-plugin-lunr/search-lunr.js" > < / script >
< script src = "gitbook/gitbook-plugin-sharing/buttons.js" > < / script >
< script src = "gitbook/gitbook-plugin-fontsettings/fontsettings.js" > < / script >
< / body >
< / html >