diff --git a/perf-test/clj/reitit/go_perf_test.clj b/perf-test/clj/reitit/go_perf_test.clj index 1e6b502b..f29f78fc 100644 --- a/perf-test/clj/reitit/go_perf_test.clj +++ b/perf-test/clj/reitit/go_perf_test.clj @@ -2,7 +2,8 @@ (:require [criterium.core :as cc] [reitit.perf-utils :refer :all] [reitit.ring :as ring] - [clojure.string :as str])) + [clojure.string :as str] + [reitit.core :as r])) ;; ;; start repl with `lein perf repl` @@ -31,46 +32,266 @@ (mapv (fn [[p d]] (if (= path p) [p (assoc d method h)] [p d])) routes) (conj routes [path {method h}])))) -(def routes [{"POST", "/1/classes/:className"}, - {"GET", "/1/classes/:className/:objectId"}, - {"PUT", "/1/classes/:className/:objectId"}, - {"GET", "/1/classes/:className"}, - {"DELETE", "/1/classes/:className/:objectId"}, +(def routes + [{"GET" "/authorizations"} + {"GET" "/authorizations/:id"} + {"POST" "/authorizations"} + ;; {"PUT" "/authorizations/clients/:client_id"} + ;; {"PATCH" "/authorizations/:id"} + {"DELETE" "/authorizations/:id"} + {"GET" "/applications/:client_id/tokens/:access_token"} + {"DELETE" "/applications/:client_id/tokens"} + {"DELETE" "/applications/:client_id/tokens/:access_token"} - ;; Users - {"POST", "/1/users"}, - {"GET", "/1/login"}, - {"GET", "/1/users/:objectId"}, - {"PUT", "/1/users/:objectId"}, - {"GET", "/1/users"}, - {"DELETE", "/1/users/:objectId"}, - {"POST", "/1/requestPasswordReset"}, + ;; Activity + {"GET" "/events"} + {"GET" "/repos/:owner/:repo/events"} + {"GET" "/networks/:owner/:repo/events"} + {"GET" "/orgs/:org/events"} + {"GET" "/users/:user/received_events"} + {"GET" "/users/:user/received_events/public"} + {"GET" "/users/:user/events"} + {"GET" "/users/:user/events/public"} + {"GET" "/users/:user/events/orgs/:org"} + {"GET" "/feeds"} + {"GET" "/notifications"} + {"GET" "/repos/:owner/:repo/notifications"} + {"PUT" "/notifications"} + {"PUT" "/repos/:owner/:repo/notifications"} + {"GET" "/notifications/threads/:id"} + ;; {"PATCH" "/notifications/threads/:id"} + {"GET" "/notifications/threads/:id/subscription"} + {"PUT" "/notifications/threads/:id/subscription"} + {"DELETE" "/notifications/threads/:id/subscription"} + {"GET" "/repos/:owner/:repo/stargazers"} + {"GET" "/users/:user/starred"} + {"GET" "/user/starred"} + {"GET" "/user/starred/:owner/:repo"} + {"PUT" "/user/starred/:owner/:repo"} + {"DELETE" "/user/starred/:owner/:repo"} + {"GET" "/repos/:owner/:repo/subscribers"} + {"GET" "/users/:user/subscriptions"} + {"GET" "/user/subscriptions"} + {"GET" "/repos/:owner/:repo/subscription"} + {"PUT" "/repos/:owner/:repo/subscription"} + {"DELETE" "/repos/:owner/:repo/subscription"} + {"GET" "/user/subscriptions/:owner/:repo"} + {"PUT" "/user/subscriptions/:owner/:repo"} + {"DELETE" "/user/subscriptions/:owner/:repo"} - ;; Roles - {"POST", "/1/roles"}, - {"GET", "/1/roles/:objectId"}, - {"PUT", "/1/roles/:objectId"}, - {"GET", "/1/roles"}, - {"DELETE", "/1/roles/:objectId"}, + ;; Gists + {"GET" "/users/:user/gists"} + {"GET" "/gists"} + ;; {"GET" "/gists/public"} + ;; {"GET" "/gists/starred"} + {"GET" "/gists/:id"} + {"POST" "/gists"} + ;; {"PATCH" "/gists/:id"} + {"PUT" "/gists/:id/star"} + {"DELETE" "/gists/:id/star"} + {"GET" "/gists/:id/star"} + {"POST" "/gists/:id/forks"} + {"DELETE" "/gists/:id"} - ;; Files - {"POST", "/1/files/:fileName"}, + ;; Git Data + {"GET" "/repos/:owner/:repo/git/blobs/:sha"} + {"POST" "/repos/:owner/:repo/git/blobs"} + {"GET" "/repos/:owner/:repo/git/commits/:sha"} + {"POST" "/repos/:owner/:repo/git/commits"} + ;; {"GET" "/repos/:owner/:repo/git/refs/*ref"} + {"GET" "/repos/:owner/:repo/git/refs"} + {"POST" "/repos/:owner/:repo/git/refs"} + ;; {"PATCH" "/repos/:owner/:repo/git/refs/*ref"} + ;; {"DELETE" "/repos/:owner/:repo/git/refs/*ref"} + {"GET" "/repos/:owner/:repo/git/tags/:sha"} + {"POST" "/repos/:owner/:repo/git/tags"} + {"GET" "/repos/:owner/:repo/git/trees/:sha"} + {"POST" "/repos/:owner/:repo/git/trees"} - ;; Analytics - {"POST", "/1/events/:eventName"}, + ;; Issues + {"GET" "/issues"} + {"GET" "/user/issues"} + {"GET" "/orgs/:org/issues"} + {"GET" "/repos/:owner/:repo/issues"} + {"GET" "/repos/:owner/:repo/issues/:number"} + {"POST" "/repos/:owner/:repo/issues"} + ;; {"PATCH" "/repos/:owner/:repo/issues/:number"} + {"GET" "/repos/:owner/:repo/assignees"} + {"GET" "/repos/:owner/:repo/assignees/:assignee"} + {"GET" "/repos/:owner/:repo/issues/:number/comments"} + ;; {"GET" "/repos/:owner/:repo/issues/comments"} + ;; {"GET" "/repos/:owner/:repo/issues/comments/:id"} + {"POST" "/repos/:owner/:repo/issues/:number/comments"} + ;; {"PATCH" "/repos/:owner/:repo/issues/comments/:id"} + ;; {"DELETE" "/repos/:owner/:repo/issues/comments/:id"} + {"GET" "/repos/:owner/:repo/issues/:number/events"} + ;; {"GET" "/repos/:owner/:repo/issues/events"} + ;; {"GET" "/repos/:owner/:repo/issues/events/:id"} + {"GET" "/repos/:owner/:repo/labels"} + {"GET" "/repos/:owner/:repo/labels/:name"} + {"POST" "/repos/:owner/:repo/labels"} + ;; {"PATCH" "/repos/:owner/:repo/labels/:name"} + {"DELETE" "/repos/:owner/:repo/labels/:name"} + {"GET" "/repos/:owner/:repo/issues/:number/labels"} + {"POST" "/repos/:owner/:repo/issues/:number/labels"} + {"DELETE" "/repos/:owner/:repo/issues/:number/labels/:name"} + {"PUT" "/repos/:owner/:repo/issues/:number/labels"} + {"DELETE" "/repos/:owner/:repo/issues/:number/labels"} + {"GET" "/repos/:owner/:repo/milestones/:number/labels"} + {"GET" "/repos/:owner/:repo/milestones"} + {"GET" "/repos/:owner/:repo/milestones/:number"} + {"POST" "/repos/:owner/:repo/milestones"} + ;; {"PATCH" "/repos/:owner/:repo/milestones/:number"} + {"DELETE" "/repos/:owner/:repo/milestones/:number"} - ;; Push Notifications - {"POST", "/1/push"}, + ;; Miscellaneous + {"GET" "/emojis"} + {"GET" "/gitignore/templates"} + {"GET" "/gitignore/templates/:name"} + {"POST" "/markdown"} + {"POST" "/markdown/raw"} + {"GET" "/meta"} + {"GET" "/rate_limit"} - ;; Installations - {"POST", "/1/installations"}, - {"GET", "/1/installations/:objectId"}, - {"PUT", "/1/installations/:objectId"}, - {"GET", "/1/installations"}, - {"DELETE", "/1/installations/:objectId"}, + ;; Organizations + {"GET" "/users/:user/orgs"} + {"GET" "/user/orgs"} + {"GET" "/orgs/:org"} + ;; {"PATCH" "/orgs/:org"} + {"GET" "/orgs/:org/members"} + {"GET" "/orgs/:org/members/:user"} + {"DELETE" "/orgs/:org/members/:user"} + {"GET" "/orgs/:org/public_members"} + {"GET" "/orgs/:org/public_members/:user"} + {"PUT" "/orgs/:org/public_members/:user"} + {"DELETE" "/orgs/:org/public_members/:user"} + {"GET" "/orgs/:org/teams"} + {"GET" "/teams/:id"} + {"POST" "/orgs/:org/teams"} + ;; {"PATCH" "/teams/:id"} + {"DELETE" "/teams/:id"} + {"GET" "/teams/:id/members"} + {"GET" "/teams/:id/members/:user"} + {"PUT" "/teams/:id/members/:user"} + {"DELETE" "/teams/:id/members/:user"} + {"GET" "/teams/:id/repos"} + {"GET" "/teams/:id/repos/:owner/:repo"} + {"PUT" "/teams/:id/repos/:owner/:repo"} + {"DELETE" "/teams/:id/repos/:owner/:repo"} + {"GET" "/user/teams"} - ;; Cloud Functions - {"POST", "/1/functions"}]) + ;; Pull Requests + {"GET" "/repos/:owner/:repo/pulls"} + {"GET" "/repos/:owner/:repo/pulls/:number"} + {"POST" "/repos/:owner/:repo/pulls"} + ;; {"PATCH" "/repos/:owner/:repo/pulls/:number"} + {"GET" "/repos/:owner/:repo/pulls/:number/commits"} + {"GET" "/repos/:owner/:repo/pulls/:number/files"} + {"GET" "/repos/:owner/:repo/pulls/:number/merge"} + {"PUT" "/repos/:owner/:repo/pulls/:number/merge"} + {"GET" "/repos/:owner/:repo/pulls/:number/comments"} + ;; {"GET" "/repos/:owner/:repo/pulls/comments"} + ;; {"GET" "/repos/:owner/:repo/pulls/comments/:number"} + {"PUT" "/repos/:owner/:repo/pulls/:number/comments"} + ;; {"PATCH" "/repos/:owner/:repo/pulls/comments/:number"} + ;; {"DELETE" "/repos/:owner/:repo/pulls/comments/:number"} + + ;; Repositories + {"GET" "/user/repos"} + {"GET" "/users/:user/repos"} + {"GET" "/orgs/:org/repos"} + {"GET" "/repositories"} + {"POST" "/user/repos"} + {"POST" "/orgs/:org/repos"} + {"GET" "/repos/:owner/:repo"} + ;; {"PATCH" "/repos/:owner/:repo"} + {"GET" "/repos/:owner/:repo/contributors"} + {"GET" "/repos/:owner/:repo/languages"} + {"GET" "/repos/:owner/:repo/teams"} + {"GET" "/repos/:owner/:repo/tags"} + {"GET" "/repos/:owner/:repo/branches"} + {"GET" "/repos/:owner/:repo/branches/:branch"} + {"DELETE" "/repos/:owner/:repo"} + {"GET" "/repos/:owner/:repo/collaborators"} + {"GET" "/repos/:owner/:repo/collaborators/:user"} + {"PUT" "/repos/:owner/:repo/collaborators/:user"} + {"DELETE" "/repos/:owner/:repo/collaborators/:user"} + {"GET" "/repos/:owner/:repo/comments"} + {"GET" "/repos/:owner/:repo/commits/:sha/comments"} + {"POST" "/repos/:owner/:repo/commits/:sha/comments"} + {"GET" "/repos/:owner/:repo/comments/:id"} + ;; {"PATCH" "/repos/:owner/:repo/comments/:id"} + {"DELETE" "/repos/:owner/:repo/comments/:id"} + {"GET" "/repos/:owner/:repo/commits"} + {"GET" "/repos/:owner/:repo/commits/:sha"} + {"GET" "/repos/:owner/:repo/readme"} + ;; {"GET" "/repos/:owner/:repo/contents/*path"} + ;; {"PUT" "/repos/:owner/:repo/contents/*path"} + ;; {"DELETE" "/repos/:owner/:repo/contents/*path"} + ;; {"GET" "/repos/:owner/:repo/:archive_format/:ref"} + {"GET" "/repos/:owner/:repo/keys"} + {"GET" "/repos/:owner/:repo/keys/:id"} + {"POST" "/repos/:owner/:repo/keys"} + ;; {"PATCH" "/repos/:owner/:repo/keys/:id"} + {"DELETE" "/repos/:owner/:repo/keys/:id"} + {"GET" "/repos/:owner/:repo/downloads"} + {"GET" "/repos/:owner/:repo/downloads/:id"} + {"DELETE" "/repos/:owner/:repo/downloads/:id"} + {"GET" "/repos/:owner/:repo/forks"} + {"POST" "/repos/:owner/:repo/forks"} + {"GET" "/repos/:owner/:repo/hooks"} + {"GET" "/repos/:owner/:repo/hooks/:id"} + {"POST" "/repos/:owner/:repo/hooks"} + ;; {"PATCH" "/repos/:owner/:repo/hooks/:id"} + {"POST" "/repos/:owner/:repo/hooks/:id/tests"} + {"DELETE" "/repos/:owner/:repo/hooks/:id"} + {"POST" "/repos/:owner/:repo/merges"} + {"GET" "/repos/:owner/:repo/releases"} + {"GET" "/repos/:owner/:repo/releases/:id"} + {"POST" "/repos/:owner/:repo/releases"} + ;; {"PATCH" "/repos/:owner/:repo/releases/:id"} + {"DELETE" "/repos/:owner/:repo/releases/:id"} + {"GET" "/repos/:owner/:repo/releases/:id/assets"} + {"GET" "/repos/:owner/:repo/stats/contributors"} + {"GET" "/repos/:owner/:repo/stats/commit_activity"} + {"GET" "/repos/:owner/:repo/stats/code_frequency"} + {"GET" "/repos/:owner/:repo/stats/participation"} + {"GET" "/repos/:owner/:repo/stats/punch_card"} + {"GET" "/repos/:owner/:repo/statuses/:ref"} + {"POST" "/repos/:owner/:repo/statuses/:ref"} + + ;; Search + {"GET" "/search/repositories"} + {"GET" "/search/code"} + {"GET" "/search/issues"} + {"GET" "/search/users"} + {"GET" "/legacy/issues/search/:owner/:repository/:state/:keyword"} + {"GET" "/legacy/repos/search/:keyword"} + {"GET" "/legacy/user/search/:keyword"} + {"GET" "/legacy/user/email/:email"} + + ;; Users + {"GET" "/users/:user"} + {"GET" "/user"} + ;; {"PATCH" "/user"} + {"GET" "/users"} + {"GET" "/user/emails"} + {"POST" "/user/emails"} + {"DELETE" "/user/emails"} + {"GET" "/users/:user/followers"} + {"GET" "/user/followers"} + {"GET" "/users/:user/following"} + {"GET" "/user/following"} + {"GET" "/user/following/:user"} + {"GET" "/users/:user/following/:target_user"} + {"PUT" "/user/following/:user"} + {"DELETE" "/user/following/:user"} + {"GET" "/users/:user/keys"} + {"GET" "/user/keys"} + {"GET" "/user/keys/:id"} + {"POST" "/user/keys"} + ;; {"PATCH" "/user/keys/:id"} + {"DELETE" "/user/keys/:id"}]) (def app @@ -78,33 +299,39 @@ (ring/router (reduce (partial add h) [] routes)))) +(defrecord Req [uri request-method]) + +(defn route->req [route] + (map->Req {:request-method (-> route keys first str/lower-case keyword) + :uri (str/replace (-> route vals first) #"\/:\w+" "/1")})) + (defn routing-test [] ;; https://github.com/julienschmidt/go-http-routing-benchmark - ;; coudn't run the GO tests, so reusing just the numbers (run on better hw?): - ;; - ;; Intel Core i5-2500K (4x 3,30GHz + Turbo Boost), CPU-governor: performance - ;; 2x 4 GiB DDR3-1333 RAM, dual-channel - ;; go version go1.3rc1 linux/amd64 - ;; Ubuntu 14.04 amd64 (Linux Kernel 3.13.0-29), fresh installation + ;; go test -bench="HttpRouter" - ;; 37ns (2.0x) - ;; 180ns (4.0x) - ;; 200ns (4.8x) - "httpRouter" + (suite "httprouter vs reitit-ring") - ;; 77ns -> 120ns (decode path-params) - ;; 700ns -> 795ns (decode path-params) - ;; 890ns -> 1000ns (decode path-params) - (title "reitit-ring") - (let [r1 (map->Request {:request-method :get, :uri "/1/users"}) - r2 (map->Request {:request-method :get, :uri "/1/classes/go"}) - r3 (map->Request {:request-method :get, :uri "/1/classes/go/123456789"})] - (assert (= {:status 200, :body "/1/users"} (app r1))) - (assert (= {:status 200, :body "/1/classes/:className"} (app r2))) - (assert (= {:status 200, :body "/1/classes/:className/:objectId"} (app r3))) - (cc/quick-bench (app r1)) - (cc/quick-bench (app r2)) - (cc/quick-bench (app r3)))) + ;; 40ns (httprouter) + ;; 140ns + (let [req (map->Req {:request-method :get, :uri "/user/repos"})] + (title "static") + (assert (= {:status 200, :body "/user/repos"} (app req))) + (cc/quick-bench (app req))) + + ;; 160ns (httprouter) + ;; 990ns + (let [req (map->Req {:request-method :get, :uri "/repos/julienschmidt/httprouter/stargazers"})] + (title "param") + (assert (= {:status 200, :body "/repos/:owner/:repo/stargazers"} (app req))) + (cc/quick-bench (app req))) + + ;; 30µs (httprouter) + ;; 190µs + (let [requests (mapv route->req routes)] + (title "all") + (cc/quick-bench + (doseq [r requests] + (app r))))) (comment (routing-test))