Go HTTP request router and web framework benchmark

Go HTTP Router Benchmark

This benchmark suite aims to compare the performance of HTTP request routers for Go by implementing the routing structure of some real world APIs. Some of the APIs are slightly adapted, since they can not be implemented 1:1 in some of the routers.

Of course the tested routers can be used for any kind of HTTP request → handler function routing, not only (REST) APIs.

Tested routers & frameworks:

Motivation

Go is a great language for web applications. Since the default request multiplexer of Go's net/http package is very simple and limited, an accordingly high number of HTTP request routers exist.

Unfortunately, most of the (early) routers use pretty bad routing algorithms. Moreover, many of them are very wasteful with memory allocations, which can become a problem in a language with Garbage Collection like Go, since every (heap) allocation results in more work for the Garbage Collector.

Lately more and more bloated frameworks pop up, outdoing one another in the number of features. This benchmark tries to measure their overhead.

Be aware that we are comparing apples and oranges here. We compare feature-rich frameworks to packages with simple routing functionality only. But since we are only interested in decent request routing, I think this is not entirely unfair. The frameworks are configured to do as little additional work as possible.

If you care about performance, this benchmark can maybe help you find the right router, which scales with your application.

Personally, I prefer slim and optimized software, which is why I implemented HttpRouter, which is also tested here. In fact, this benchmark suite started as part of the packages tests, but was then extended to a generic benchmark suite. So keep in mind, that I am not completely unbiased 😌

Results

Benchmark System:

  • 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

Memory Consumption

Besides the micro-benchmarks, there are 3 sets of benchmarks where we play around with clones of some real-world APIs, and one benchmark with static routes only, to allow a comparison with http.ServeMux. The following table shows the memory required only for loading the routing structure for the respective API. The best 3 values for each test are bold. I'm pretty sure you can detect a pattern 😉

Router Static GitHub Google+ Parse
HttpServeMux 18064 B - - -
Beego 79472 B 497248 B 26480 B 38768 B
Denco 44752 B 107632 B 54896 B 36368 B
Gocraft Web 57976 B 95736 B 8024 B 13120 B
Goji 32400 B 58424 B 3392 B 6704 B
Go-Json-Rest 152608 B 148352 B 11696 B 13712 B
Gorilla Mux 685152 B 1557216 B 80240 B 125480 B
HttpRouter 26232 B 44344 B 3144 B 5792 B
HttpTreeMux 75624 B 81408 B 7712 B 7616 B
Kocha 130336 B 811744 B 139968 B 191632 B
Martini 312592 B 579472 B 27520 B 50608 B
Pat 21272 B 18968 B 1448 B 2360 B
TigerTonic 85264 B 99392 B 10576 B 11008 B
Traffic 649568 B 1124704 B 57984 B 98168 B

The first place goes to Pat, followed by HttpRouter and Goji. Now, before everyone starts reading the documentation of Pat, [SPOILER] this low memory consumption comes at the price of relatively bad routing performance. The routing structure of Pat is simple - probably too simple. [/SPOILER].

Moreover main memory is cheap and usually not a scarce resource. As long as the router doesn't require Megabytes of memory, it should be no deal breaker. But it gives us a first hint how efficient or wasteful a router works.

Static Routes

The Static benchmark is not really a clone of a real-world API. It is just a collection of random static paths inspired by the structure of the Go directory. It might not be a realistic URL-structure.

The only intention of this benchmark is to allow a comparison with the default router of Go's net/http package, http.ServeMux, which is limited to static routes and does not support parameters in the route pattern.

In the StaticAll benchmark each of 157 URLs is called once per repetition (op, operation). If you are unfamiliar with the go test -bench tool, the first number is the number of repetitions the go test tool made, to get a test running long enough for measurements. The second column shows the time in nanoseconds that a single repetition takes. The third number is the amount of heap memory allocated in bytes, the last one the average number of allocations made per repetition.

The logs below show, that http.ServeMux has only medium performance, compared to more feature-rich routers. The fastest router only needs 1.8% of the time http.ServeMux needs.

HttpRouter was the first router (I know of) that managed to serve all the static URLs without a single heap allocation. Since the first run of this benchmark more routers followed this trend and were optimized in the same way.

BenchmarkHttpServeMux_StaticAll         5000     706222 ns/op          96 B/op        6 allocs/op

BenchmarkBeego_StaticAll                2000    1408954 ns/op      482433 B/op    14088 allocs/op
BenchmarkDenco_StaticAll              200000      12679 ns/op           0 B/op        0 allocs/op
BenchmarkGocraftWeb_StaticAll          10000     154142 ns/op       51468 B/op      947 allocs/op
BenchmarkGoji_StaticAll                20000      80518 ns/op           0 B/op        0 allocs/op
BenchmarkGoJsonRest_StaticAll           2000     978164 ns/op      180973 B/op     3945 allocs/op
BenchmarkGorillaMux_StaticAll           1000    1763690 ns/op       71804 B/op      956 allocs/op
BenchmarkHttpRouter_StaticAll         100000      15010 ns/op           0 B/op        0 allocs/op
BenchmarkHttpTreeMux_StaticAll        100000      15123 ns/op           0 B/op        0 allocs/op
BenchmarkKocha_StaticAll              100000      23093 ns/op           0 B/op        0 allocs/op
BenchmarkMartini_StaticAll               500    3444278 ns/op      156015 B/op     2351 allocs/op
BenchmarkPat_StaticAll                  1000    1640745 ns/op      549187 B/op    11186 allocs/op
BenchmarkTigerTonic_StaticAll          50000      58264 ns/op        7714 B/op      157 allocs/op
BenchmarkTraffic_StaticAll               500    7230129 ns/op     3763731 B/op    27453 allocs/op

Micro Benchmarks

The following benchmarks measure the cost of some very basic operations.

In the first benchmark, only a single route, containing a parameter, is loaded into the routers. Then a request for a URL matching this pattern is made and the router has to call the respective registered handler function. End.

BenchmarkBeego_Param                  500000       5495 ns/op        1165 B/op       14 allocs/op
BenchmarkDenco_Param                 5000000        312 ns/op          50 B/op        2 allocs/op
BenchmarkGocraftWeb_Param            1000000       1440 ns/op         684 B/op        9 allocs/op
BenchmarkGoji_Param                  5000000        748 ns/op         340 B/op        2 allocs/op
BenchmarkGoJsonRest_Param             500000       6980 ns/op        1787 B/op       29 allocs/op
BenchmarkGorillaMux_Param            1000000       2665 ns/op         780 B/op        7 allocs/op
BenchmarkHttpRouter_Param           20000000        139 ns/op          33 B/op        1 allocs/op
BenchmarkHttpTreeMux_Param           5000000        558 ns/op         340 B/op        2 allocs/op
BenchmarkKocha_Param                 5000000        377 ns/op          58 B/op        2 allocs/op
BenchmarkMartini_Param                500000       6265 ns/op        1251 B/op       12 allocs/op
BenchmarkPat_Param                   1000000       1620 ns/op         670 B/op       11 allocs/op
BenchmarkTigerTonic_Param            1000000       2766 ns/op        1015 B/op       18 allocs/op
BenchmarkTraffic_Param                500000       4440 ns/op        2013 B/op       22 allocs/op

Same as before, but now with multiple parameters, all in the same single route. The intention is to see how the routers scale with the number of parameters. The values of the parameters must be passed to the handler function somehow, which requires allocations. Let's see how clever the routers solve this task with a route containing 5 and 20 parameters:

BenchmarkBeego_Param5                 100000      18473 ns/op        1291 B/op       14 allocs/op
BenchmarkDenco_Param5                2000000        982 ns/op         405 B/op        5 allocs/op
BenchmarkGocraftWeb_Param5           1000000       2218 ns/op         957 B/op       12 allocs/op
BenchmarkGoji_Param5                 1000000       1093 ns/op         340 B/op        2 allocs/op
BenchmarkGoJsonRest_Param5            200000      10462 ns/op        3264 B/op       40 allocs/op
BenchmarkGorillaMux_Param5            500000       4680 ns/op         906 B/op        7 allocs/op
BenchmarkHttpRouter_Param5           5000000        319 ns/op         162 B/op        1 allocs/op
BenchmarkHttpTreeMux_Param5          2000000        898 ns/op         340 B/op        2 allocs/op
BenchmarkKocha_Param5                1000000       1326 ns/op         448 B/op        7 allocs/op
BenchmarkMartini_Param5               200000      13027 ns/op        1376 B/op       12 allocs/op
BenchmarkPat_Param5                   500000       3416 ns/op        1435 B/op       18 allocs/op
BenchmarkTigerTonic_Param5            200000       9247 ns/op        2568 B/op       41 allocs/op
BenchmarkTraffic_Param5               500000       7206 ns/op        2312 B/op       26 allocs/op

BenchmarkBeego_Param20                 10000     106746 ns/op        3681 B/op       17 allocs/op
BenchmarkDenco_Param20               1000000       2882 ns/op        1666 B/op        7 allocs/op
BenchmarkGocraftWeb_Param20           500000       7156 ns/op        3857 B/op       16 allocs/op
BenchmarkGoji_Param20                1000000       3197 ns/op        1260 B/op        2 allocs/op
BenchmarkGoJsonRest_Param20           100000      25809 ns/op       10605 B/op       75 allocs/op
BenchmarkGorillaMux_Param20           200000       9885 ns/op        3295 B/op        9 allocs/op
BenchmarkHttpRouter_Param20          2000000        954 ns/op         646 B/op        1 allocs/op
BenchmarkHttpTreeMux_Param20          500000       5016 ns/op        2216 B/op        4 allocs/op
BenchmarkKocha_Param20                500000       4268 ns/op        1836 B/op       17 allocs/op
BenchmarkMartini_Param20               50000      55039 ns/op        3765 B/op       14 allocs/op
BenchmarkPat_Param20                  500000       3412 ns/op        1435 B/op       18 allocs/op
BenchmarkTigerTonic_Param20            50000      36825 ns/op       10710 B/op      131 allocs/op
BenchmarkTraffic_Param20              100000      22605 ns/op        8077 B/op       49 allocs/op

Now let's see how expensive it is to access a parameter. The handler function reads the value (by the name of the parameter, e.g. with a map lookup; depends on the router) and writes it to our web scale storage (/dev/null).

BenchmarkBeego_ParamWrite             500000       6604 ns/op        1602 B/op       18 allocs/op
BenchmarkDenco_ParamWrite            5000000        377 ns/op          50 B/op        2 allocs/op
BenchmarkGocraftWeb_ParamWrite       1000000       1590 ns/op         693 B/op        9 allocs/op
BenchmarkGoji_ParamWrite             2000000        818 ns/op         340 B/op        2 allocs/op
BenchmarkGoJsonRest_ParamWrite        200000       8388 ns/op        2265 B/op       33 allocs/op
BenchmarkGorillaMux_ParamWrite       1000000       2913 ns/op         780 B/op        7 allocs/op
BenchmarkHttpRouter_ParamWrite      10000000        193 ns/op          33 B/op        1 allocs/op
BenchmarkHttpTreeMux_ParamWrite      5000000        649 ns/op         340 B/op        2 allocs/op
BenchmarkKocha_ParamWrite            5000000        435 ns/op          58 B/op        2 allocs/op
BenchmarkMartini_ParamWrite           500000       7538 ns/op        1359 B/op       15 allocs/op
BenchmarkPat_ParamWrite              1000000       2940 ns/op        1109 B/op       15 allocs/op
BenchmarkTigerTonic_ParamWrite        500000       4639 ns/op        1471 B/op       23 allocs/op
BenchmarkTraffic_ParamWrite           500000       5855 ns/op        2435 B/op       25 allocs/op

Parse.com

Enough of the micro benchmark stuff. Let's play a bit with real APIs. In the first set of benchmarks, we use a clone of the structure of Parse's decent medium-sized REST API, consisting of 26 routes.

The tasks are 1.) routing a static URL (no parameters), 2.) routing a URL containing 1 parameter, 3.) same with 2 parameters, 4.) route all of the routes once (like the StaticAll benchmark, but the routes now contain parameters).

Worth noting is, that the requested route might be a good case for some routing algorithms, while it is a bad case for another algorithm. The values might vary slightly depending on the selected route.

BenchmarkBeego_ParseStatic            500000       3461 ns/op        1247 B/op       15 allocs/op
BenchmarkDenco_ParseStatic          50000000         42.6 ns/op         0 B/op        0 allocs/op
BenchmarkGocraftWeb_ParseStatic      2000000        889 ns/op         328 B/op        6 allocs/op
BenchmarkGoji_ParseStatic            5000000        341 ns/op           0 B/op        0 allocs/op
BenchmarkGoJsonRest_ParseStatic       500000       5860 ns/op        1136 B/op       25 allocs/op
BenchmarkGorillaMux_ParseStatic      1000000       2760 ns/op         456 B/op        6 allocs/op
BenchmarkHttpRouter_ParseStatic     50000000         36.7 ns/op         0 B/op        0 allocs/op
BenchmarkHttpTreeMux_ParseStatic    50000000         62.6 ns/op         0 B/op        0 allocs/op
BenchmarkKocha_ParseStatic          50000000         72.2 ns/op         0 B/op        0 allocs/op
BenchmarkMartini_ParseStatic          500000       5528 ns/op         927 B/op       11 allocs/op
BenchmarkPat_ParseStatic             2000000        809 ns/op         246 B/op        5 allocs/op
BenchmarkTigerTonic_ParseStatic     10000000        264 ns/op          49 B/op        1 allocs/op
BenchmarkTraffic_ParseStatic          500000       5008 ns/op        2377 B/op       24 allocs/op

BenchmarkBeego_ParseParam             500000       7983 ns/op        1775 B/op       28 allocs/op
BenchmarkDenco_ParseParam            5000000        347 ns/op          50 B/op        2 allocs/op
BenchmarkGocraftWeb_ParseParam       1000000       1535 ns/op         700 B/op        9 allocs/op
BenchmarkGoji_ParseParam             2000000        983 ns/op         340 B/op        2 allocs/op
BenchmarkGoJsonRest_ParseParam        500000       7208 ns/op        1789 B/op       29 allocs/op
BenchmarkGorillaMux_ParseParam       1000000       3186 ns/op         780 B/op        7 allocs/op
BenchmarkHttpRouter_ParseParam      10000000        178 ns/op          65 B/op        1 allocs/op
BenchmarkHttpTreeMux_ParseParam      5000000        617 ns/op         340 B/op        2 allocs/op
BenchmarkKocha_ParseParam            5000000        413 ns/op          58 B/op        2 allocs/op
BenchmarkMartini_ParseParam           500000       7524 ns/op        1251 B/op       12 allocs/op
BenchmarkPat_ParseParam              1000000       2707 ns/op        1160 B/op       18 allocs/op
BenchmarkTigerTonic_ParseParam       1000000       3010 ns/op        1048 B/op       19 allocs/op
BenchmarkTraffic_ParseParam           500000       5228 ns/op        2314 B/op       24 allocs/op

BenchmarkBeego_Parse2Params           200000       9217 ns/op        1935 B/op       28 allocs/op
BenchmarkDenco_Parse2Params          5000000        542 ns/op         115 B/op        3 allocs/op
BenchmarkGocraftWeb_Parse2Params     1000000       1756 ns/op         750 B/op       10 allocs/op
BenchmarkGoji_Parse2Params           2000000        954 ns/op         340 B/op        2 allocs/op
BenchmarkGoJsonRest_Parse2Params      500000       8131 ns/op        2145 B/op       32 allocs/op
BenchmarkGorillaMux_Parse2Params      500000       3623 ns/op         812 B/op        7 allocs/op
BenchmarkHttpRouter_Parse2Params    10000000        202 ns/op          65 B/op        1 allocs/op
BenchmarkHttpTreeMux_Parse2Params    5000000        708 ns/op         340 B/op        2 allocs/op
BenchmarkKocha_Parse2Params          5000000        666 ns/op         132 B/op        4 allocs/op
BenchmarkMartini_Parse2Params         200000       7723 ns/op        1283 B/op       12 allocs/op
BenchmarkPat_Parse2Params            1000000       2687 ns/op         887 B/op       19 allocs/op
BenchmarkTigerTonic_Parse2Params      500000       4720 ns/op        1473 B/op       28 allocs/op
BenchmarkTraffic_Parse2Params         500000       5467 ns/op        2120 B/op       24 allocs/op

BenchmarkBeego_ParseAll                10000     197920 ns/op       38877 B/op      616 allocs/op
BenchmarkDenco_ParseAll               500000       7692 ns/op        1000 B/op       35 allocs/op
BenchmarkGocraftWeb_ParseAll           50000      36226 ns/op       14639 B/op      208 allocs/op
BenchmarkGoji_ParseAll                100000      19721 ns/op        5448 B/op       32 allocs/op
BenchmarkGoJsonRest_ParseAll           10000     180128 ns/op       41202 B/op      727 allocs/op
BenchmarkGorillaMux_ParseAll           10000     120929 ns/op       17138 B/op      173 allocs/op
BenchmarkHttpRouter_ParseAll          500000       3592 ns/op         660 B/op       16 allocs/op
BenchmarkHttpTreeMux_ParseAll         200000      11650 ns/op        5452 B/op       32 allocs/op
BenchmarkKocha_ParseAll               200000       9371 ns/op        1163 B/op       44 allocs/op
BenchmarkMartini_ParseAll              10000     200307 ns/op       29375 B/op      305 allocs/op
BenchmarkPat_ParseAll                  50000      53113 ns/op       18017 B/op      363 allocs/op
BenchmarkTigerTonic_ParseAll           50000      67208 ns/op       20547 B/op      419 allocs/op
BenchmarkTraffic_ParseAll              10000     164938 ns/op       70161 B/op      743 allocs/op

GitHub

The GitHub API is rather large, consisting of 203 routes. The tasks are basically the same as in the benchmarks before.

BenchmarkBeego_GithubStatic           500000       3880 ns/op        1148 B/op       31 allocs/op
BenchmarkDenco_GithubStatic         50000000         60.5 ns/op         0 B/op        0 allocs/op
BenchmarkGocraftWeb_GithubStatic     2000000        933 ns/op         328 B/op        6 allocs/op
BenchmarkGoji_GithubStatic           5000000        401 ns/op           0 B/op        0 allocs/op
BenchmarkGoJsonRest_GithubStatic      500000       6006 ns/op        1150 B/op       25 allocs/op
BenchmarkGorillaMux_GithubStatic      100000      18227 ns/op         456 B/op        6 allocs/op
BenchmarkHttpRouter_GithubStatic    50000000         63.2 ns/op         0 B/op        0 allocs/op
BenchmarkHttpTreeMux_GithubStatic   50000000         65.1 ns/op         0 B/op        0 allocs/op
BenchmarkKocha_GithubStatic         20000000         99.5 ns/op         0 B/op        0 allocs/op
BenchmarkMartini_GithubStatic         100000      18546 ns/op         927 B/op       11 allocs/op
BenchmarkPat_GithubStatic             200000      11503 ns/op        3754 B/op       76 allocs/op
BenchmarkTigerTonic_GithubStatic     5000000        308 ns/op          49 B/op        1 allocs/op
BenchmarkTraffic_GithubStatic          50000      44923 ns/op       23105 B/op      168 allocs/op

BenchmarkBeego_GithubParam             50000      44645 ns/op        2973 B/op       50 allocs/op
BenchmarkDenco_GithubParam           5000000        643 ns/op         115 B/op        3 allocs/op
BenchmarkGocraftWeb_GithubParam      1000000       1855 ns/op         750 B/op       10 allocs/op
BenchmarkGoji_GithubParam            1000000       1314 ns/op         340 B/op        2 allocs/op
BenchmarkGoJsonRest_GithubParam       200000       8427 ns/op        2159 B/op       32 allocs/op
BenchmarkGorillaMux_GithubParam       200000      11485 ns/op         813 B/op        7 allocs/op
BenchmarkHttpRouter_GithubParam      5000000        304 ns/op          97 B/op        1 allocs/op
BenchmarkHttpTreeMux_GithubParam     2000000        770 ns/op         340 B/op        2 allocs/op
BenchmarkKocha_GithubParam           5000000        754 ns/op         132 B/op        4 allocs/op
BenchmarkMartini_GithubParam          100000      22637 ns/op        1284 B/op       12 allocs/op
BenchmarkPat_GithubParam              500000       7319 ns/op        2538 B/op       44 allocs/op
BenchmarkTigerTonic_GithubParam       500000       4722 ns/op        1467 B/op       26 allocs/op
BenchmarkTraffic_GithubParam          100000      18700 ns/op        7076 B/op       58 allocs/op

BenchmarkBeego_GithubAll                 100   23430165 ns/op      502614 B/op     9871 allocs/op
BenchmarkDenco_GithubAll               10000     120365 ns/op       21219 B/op      506 allocs/op
BenchmarkGocraftWeb_GithubAll           5000     358982 ns/op      139091 B/op     1903 allocs/op
BenchmarkGoji_GithubAll                 5000     604522 ns/op       56896 B/op      340 allocs/op
BenchmarkGoJsonRest_GithubAll           1000    1645794 ns/op      404222 B/op     6303 allocs/op
BenchmarkGorillaMux_GithubAll            500    6634737 ns/op      152277 B/op     1402 allocs/op
BenchmarkHttpRouter_GithubAll          50000      51138 ns/op       14039 B/op      168 allocs/op
BenchmarkHttpTreeMux_GithubAll         10000     132507 ns/op       56907 B/op      340 allocs/op
BenchmarkKocha_GithubAll               10000     143398 ns/op       24117 B/op      676 allocs/op
BenchmarkMartini_GithubAll               200    9802351 ns/op      258349 B/op     2713 allocs/op
BenchmarkPat_GithubAll                   500    4154815 ns/op     1539081 B/op    24970 allocs/op
BenchmarkTigerTonic_GithubAll           2000     920839 ns/op      247085 B/op     5171 allocs/op
BenchmarkTraffic_GithubAll               200    8087393 ns/op     3143039 B/op    23958 allocs/op

Google+

Last but not least the Google+ API, consisting of 13 routes. In reality this is just a subset of a much larger API.

BenchmarkBeego_GPlusStatic           1000000       2321 ns/op         808 B/op       11 allocs/op
BenchmarkDenco_GPlusStatic          50000000         37.2 ns/op         0 B/op        0 allocs/op
BenchmarkGocraftWeb_GPlusStatic      2000000        862 ns/op         312 B/op        6 allocs/op
BenchmarkGoji_GPlusStatic           10000000        270 ns/op           0 B/op        0 allocs/op
BenchmarkGoJsonRest_GPlusStatic       500000       5827 ns/op        1136 B/op       25 allocs/op
BenchmarkGorillaMux_GPlusStatic      1000000       1793 ns/op         456 B/op        6 allocs/op
BenchmarkHttpRouter_GPlusStatic     50000000         34.6 ns/op         0 B/op        0 allocs/op
BenchmarkHttpTreeMux_GPlusStatic    50000000         35.4 ns/op         0 B/op        0 allocs/op
BenchmarkKocha_GPlusStatic          50000000         63.8 ns/op         0 B/op        0 allocs/op
BenchmarkMartini_GPlusStatic          500000       4887 ns/op         927 B/op       11 allocs/op
BenchmarkPat_GPlusStatic             5000000        336 ns/op          98 B/op        2 allocs/op
BenchmarkTigerTonic_GPlusStatic     10000000        186 ns/op          33 B/op        1 allocs/op
BenchmarkTraffic_GPlusStatic          500000       3350 ns/op        1503 B/op       18 allocs/op

BenchmarkBeego_GPlusParam             200000       7657 ns/op        1231 B/op       16 allocs/op
BenchmarkDenco_GPlusParam            5000000        365 ns/op          50 B/op        2 allocs/op
BenchmarkGocraftWeb_GPlusParam       1000000       1519 ns/op         684 B/op        9 allocs/op
BenchmarkGoji_GPlusParam             2000000        889 ns/op         340 B/op        2 allocs/op
BenchmarkGoJsonRest_GPlusParam        500000       7388 ns/op        1806 B/op       29 allocs/op
BenchmarkGorillaMux_GPlusParam        500000       4040 ns/op         780 B/op        7 allocs/op
BenchmarkHttpRouter_GPlusParam      10000000        203 ns/op          65 B/op        1 allocs/op
BenchmarkHttpTreeMux_GPlusParam      5000000        638 ns/op         340 B/op        2 allocs/op
BenchmarkKocha_GPlusParam            5000000        444 ns/op          58 B/op        2 allocs/op
BenchmarkMartini_GPlusParam           200000       8672 ns/op        1251 B/op       12 allocs/op
BenchmarkPat_GPlusParam              1000000       1895 ns/op         719 B/op       12 allocs/op
BenchmarkTigerTonic_GPlusParam       1000000       3166 ns/op        1085 B/op       18 allocs/op
BenchmarkTraffic_GPlusParam           500000       5369 ns/op        2030 B/op       22 allocs/op

BenchmarkBeego_GPlus2Params           200000       9999 ns/op        1293 B/op       16 allocs/op
BenchmarkDenco_GPlus2Params          5000000        618 ns/op         115 B/op        3 allocs/op
BenchmarkGocraftWeb_GPlus2Params     1000000       1860 ns/op         750 B/op       10 allocs/op
BenchmarkGoji_GPlus2Params           1000000       1296 ns/op         340 B/op        2 allocs/op
BenchmarkGoJsonRest_GPlus2Params      200000       8516 ns/op        2178 B/op       32 allocs/op
BenchmarkGorillaMux_GPlus2Params      200000       9007 ns/op         812 B/op        7 allocs/op
BenchmarkHttpRouter_GPlus2Params    10000000        246 ns/op          65 B/op        1 allocs/op
BenchmarkHttpTreeMux_GPlus2Params    2000000        751 ns/op         340 B/op        2 allocs/op
BenchmarkKocha_GPlus2Params          5000000        744 ns/op         132 B/op        4 allocs/op
BenchmarkMartini_GPlus2Params         100000      27700 ns/op        1382 B/op       16 allocs/op
BenchmarkPat_GPlus2Params             500000       5981 ns/op        2347 B/op       34 allocs/op
BenchmarkTigerTonic_GPlus2Params      500000       5076 ns/op        1561 B/op       27 allocs/op
BenchmarkTraffic_GPlus2Params         200000      12711 ns/op        3599 B/op       34 allocs/op

BenchmarkBeego_GPlusAll                10000     113601 ns/op       15897 B/op      217 allocs/op
BenchmarkDenco_GPlusAll               500000       5761 ns/op         880 B/op       27 allocs/op
BenchmarkGocraftWeb_GPlusAll          100000      20527 ns/op        8513 B/op      116 allocs/op
BenchmarkGoji_GPlusAll                200000      12312 ns/op        3746 B/op       22 allocs/op
BenchmarkGoJsonRest_GPlusAll           20000      99250 ns/op       23871 B/op      386 allocs/op
BenchmarkGorillaMux_GPlusAll           50000      63046 ns/op        9655 B/op       90 allocs/op
BenchmarkHttpRouter_GPlusAll         1000000       2513 ns/op         655 B/op       11 allocs/op
BenchmarkHttpTreeMux_GPlusAll         500000       7706 ns/op        3748 B/op       22 allocs/op
BenchmarkKocha_GPlusAll               500000       6858 ns/op        1017 B/op       35 allocs/op
BenchmarkMartini_GPlusAll              10000     155402 ns/op       16368 B/op      179 allocs/op
BenchmarkPat_GPlusAll                  50000      47397 ns/op       17270 B/op      302 allocs/op
BenchmarkTigerTonic_GPlusAll           50000      49864 ns/op       15160 B/op      311 allocs/op
BenchmarkTraffic_GPlusAll              10000     108007 ns/op       41779 B/op      430 allocs/op

Conclusions

First of all, there is no reason to use net/http's default ServeMux, which is very limited and does not have especially good performance. There are enough alternatives coming in every flavor, choose the one you like best.

Secondly, the broad range of functions of some of the frameworks comes at a high price in terms of performance. For example Martini has great flexibility, but very bad performance. Martini has the worst performance of all tested routers in a lot of the benchmarks. Beego seems to have some scalability problems and easily defeats Martini with even worse performance, when the number of parameters or routes is high. I really hope, that the routing of these packages can be optimized. I think the Go-ecosystem needs great feature-rich frameworks like these.

Last but not least, we have to determine the performance champion.

Denco and its predecessor Kocha-urlrouter seem to have great performance, but are not convenient to use as a router for the net/http package. A lot of extra work is necessary to use it as a http.Handler. The README of Denco claims, that the package is not intended as a replacement for http.ServeMux.

Goji looks very decent. It has great performance while also having a great range of features, more than any other router / framework in the top group.

Currently no router can beat the performance of the HttpRouter package, which currently dominates nearly all benchmarks.

In the end, performance can not be the (only) criterion for choosing a router. Play around a bit with some of the routers, and choose the one you like best.

Usage

If you'd like to run these benchmarks locally, you'll need to install the package first:

go get github.com/julienschmidt/go-http-routing-benchmark

This may take a while due to the large number of dependencies that need to be downloaded. Once that command has finished you can run the full set of benchmarks like this:

cd $GOPATH/src/github.com/julienschmidt/go-http-routing-benchmark
go test -bench=.

Note: If you run the tests and it SIGQUIT's make the go test timeout longer (#44)

go test -timeout=2h -bench=.

You can bench specific frameworks only by using a regular expression as the value of the bench parameter:

go test -bench="Martini|Gin|HttpMux"
Comments
  • Bear benchmark triggers panic

    Bear benchmark triggers panic

    Somehow the bear test gets a nil request passed. When I dump the requests the last several tests are the following:

    &{POST /1/events/:eventName HTTP/1.1 1 1 map[] <nil> 0 [] false  map[] map[] <nil> map[]  /1/events/:eventName <nil> <nil>}
    &{POST /1/push HTTP/1.1 1 1 map[] <nil> 0 [] false  map[] map[] <nil> map[]  /1/push <nil> <nil>}
    &{POST /1/installations HTTP/1.1 1 1 map[] <nil> 0 [] false  map[] map[] <nil> map[]  /1/installations <nil> <nil>}
    &{GET /1/installations/:objectId HTTP/1.1 1 1 map[] <nil> 0 [] false  map[] map[] <nil> map[]  /1/installations/:objectId <nil> <nil>}
    &{PUT /1/installations/:objectId HTTP/1.1 1 1 map[] <nil> 0 [] false  map[] map[] <nil> map[]  /1/installations/:objectId <nil> <nil>}
    &{GET /1/installations HTTP/1.1 1 1 map[] <nil> 0 [] false  map[] map[] <nil> map[]  /1/installations <nil> <nil>}
    &{DELETE /1/installations/:objectId HTTP/1.1 1 1 map[] <nil> 0 [] false  map[] map[] <nil> map[]  /1/installations/:objectId <nil> <nil>}
    &{POST /1/functions HTTP/1.1 1 1 map[] <nil> 0 [] false  map[] map[] <nil> map[]  /1/functions <nil> <nil>}
    <nil>
    
    panic: runtime error: invalid memory address or nil pointer dereference [recovered]
        panic: runtime error: invalid memory address or nil pointer dereference
    [signal 0xb code=0x1 addr=0x0 pc=0x489482]
    
    goroutine 8 [running]:
    testing.tRunner.func1(0xc82008a000)
        /usr/local/go/src/testing/testing.go:450 +0x171
    github.com/julienschmidt/go-http-routing-benchmark.bearHandlerTest(0x0, 0x0, 0x0, 0xc8202cb4c0)
        /data01/dev/src/github.com/julienschmidt/go-http-routing-benchmark/routers.go:144 +0x1a2
    github.com/ursiform/bear.(*Context).Next(0xc8202cb4c0)
        /data01/dev/src/github.com/ursiform/bear/context.go:48 +0x11e
    github.com/ursiform/bear.(*Mux).ServeHTTP(0xc8202b64e0, 0x7fb2acef7e08, 0xc8202cc420, 0xc82010a380)
        /data01/dev/src/github.com/ursiform/bear/mux.go:138 +0x19d
    github.com/julienschmidt/go-http-routing-benchmark.TestRouters(0xc82008a000)
        /data01/dev/src/github.com/julienschmidt/go-http-routing-benchmark/routers_test.go:73 +0x52a
    testing.tRunner(0xc82008a000, 0x10f3620)
        /usr/local/go/src/testing/testing.go:456 +0x98
    created by testing.RunTests
        /usr/local/go/src/testing/testing.go:561 +0x86d
    
    goroutine 1 [chan receive]:
    testing.RunTests(0xe47108, 0x10f3620, 0x1, 0x1, 0x42ca01)
        /usr/local/go/src/testing/testing.go:562 +0x8ad
    testing.(*M).Run(0xc820167ee8, 0x0)
        /usr/local/go/src/testing/testing.go:494 +0x70
    main.main()
        github.com/julienschmidt/go-http-routing-benchmark/_test/_testmain.go:888 +0x116
    
    goroutine 17 [syscall, locked to thread]:
    runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:1696 +0x1
    
    goroutine 5 [syscall]:
    os/signal.loop()
        /usr/local/go/src/os/signal/signal_unix.go:22 +0x18
    created by os/signal.init.1
        /usr/local/go/src/os/signal/signal_unix.go:28 +0x37
    exit status 2
    FAIL    github.com/julienschmidt/go-http-routing-benchmark  0.044s
    
  • It is fair to adjust URLs?

    It is fair to adjust URLs?

    I have idea to make a router based on hashtable. There is only one problem, how to distinguish parameters and 'static' elements in real paths (eg. in /github.com/julienschmidt/go-http-routing-benchmark/issues/new params are emphasized, rest is static).

    There is simple solution, prefix each param with colon in real URL ( /github.com/:julienschmidt/:go-http-routing-benchmark/issues/new ) - it is only a little bit more verbose, but has nice computional properties :)

    Do you (or any competiotor?) consider using this as a cheat in competition with other routing implementations?

  • SIGQUIT when running with -bench=.

    SIGQUIT when running with -bench=.

    Here's a problem I've now run into consistently running the benchmarks using go 1.4.2 and 1.5 (master):

    http://pastebin.com/K1QweDns

    The SIGQUIT occurs in different places each time, although the stack dumps often mention Beego.

    Significantly, these problems were only seen using virtualized Linux instances (VMWare, VirtualBox). Have you seen similar stack dumps running the full suite?

  • add new framework Aero

    add new framework Aero

    Result 🔴

    C:\Users\<Comnam>\dev\go-http-routing-benchmark>go test -bench="Aero"
    #GithubAPI Routes: 203
       Aero: 1290112 Bytes
    
    #GPlusAPI Routes: 13
       Aero: 87960 Bytes
    
    #ParseAPI Routes: 26
       Aero: 163736 Bytes
    
    #Static Routes: 157
       Aero: 800408 Bytes
    
    goos: windows
    goarch: amd64
    pkg: github.com/julienschmidt/go-http-routing-benchmark
    BenchmarkAero_Param             20000000                59.8 ns/op             0 B/op          0 allocs/op
    BenchmarkAero_Param5            20000000               104 ns/op               0 B/op          0 allocs/op
    BenchmarkAero_Param20           50000000                37.7 ns/op             0 B/op          0 allocs/op
    BenchmarkAero_ParamWrite        20000000                89.6 ns/op             0 B/op          0 allocs/op
    BenchmarkAero_GithubStatic      30000000                59.2 ns/op             0 B/op          0 allocs/op
    BenchmarkAero_GithubParam       10000000               128 ns/op               0 B/op          0 allocs/op
    BenchmarkAero_GithubAll            50000             28990 ns/op               0 B/op          0 allocs/op
    BenchmarkAero_GPlusStatic       30000000                57.8 ns/op             0 B/op          0 allocs/op
    BenchmarkAero_GPlusParam        20000000               107 ns/op               0 B/op          0 allocs/op
    BenchmarkAero_GPlus2Params      10000000               146 ns/op               0 B/op          0 allocs/op
    BenchmarkAero_GPlusAll           1000000              1388 ns/op               0 B/op          0 allocs/op
    BenchmarkAero_ParseStatic       30000000                53.6 ns/op             0 B/op          0 allocs/op
    BenchmarkAero_ParseParam        20000000                66.9 ns/op             0 B/op          0 allocs/op
    BenchmarkAero_Parse2Params      20000000                99.4 ns/op             0 B/op          0 allocs/op
    BenchmarkAero_ParseAll           1000000              2323 ns/op               0 B/op          0 allocs/op
    BenchmarkAero_StaticAll           100000             15820 ns/op               0 B/op          0 allocs/op
    PASS
    ok      github.com/julienschmidt/go-http-routing-benchmark      31.037s
    
    
  • Broken go get

    Broken go get

    After run a go get -u -v I got:

    ➜ go get -u -v
    github.com/julienschmidt/go-http-routing-benchmark (download)
    github.com/ant0ine/go-json-rest (download)
    github.com/astaxie/beego (download)
    github.com/bmizerany/pat (download)
    github.com/dimfeld/httptreemux (download)
    github.com/emicklei/go-restful (download)
    github.com/gin-gonic/gin (download)
    github.com/gin-contrib/sse (download)
    github.com/golang/protobuf (download)
    github.com/ugorji/go (download)
    Fetching https://gopkg.in/go-playground/validator.v8?go-get=1
    Parsing meta tags from https://gopkg.in/go-playground/validator.v8?go-get=1 (status code 200)
    get "gopkg.in/go-playground/validator.v8": found meta tag get.metaImport{Prefix:"gopkg.in/go-playground/validator.v8", VCS:"git", RepoRoot:"https://gopkg.in/go-playground/validator.v8"} at https://gopkg.in/go-playground/validator.v8?go-get=1
    gopkg.in/go-playground/validator.v8 (download)
    Fetching https://gopkg.in/yaml.v2?go-get=1
    Parsing meta tags from https://gopkg.in/yaml.v2?go-get=1 (status code 200)
    get "gopkg.in/yaml.v2": found meta tag get.metaImport{Prefix:"gopkg.in/yaml.v2", VCS:"git", RepoRoot:"https://gopkg.in/yaml.v2"} at https://gopkg.in/yaml.v2?go-get=1
    gopkg.in/yaml.v2 (download)
    github.com/mattn/go-isatty (download)
    github.com/go-macaron/macaron (download)
    github.com/Unknwon/com (download)
    github.com/go-macaron/inject (download)
    Fetching https://golang.org/x/crypto/pbkdf2?go-get=1
    Parsing meta tags from https://golang.org/x/crypto/pbkdf2?go-get=1 (status code 200)
    get "golang.org/x/crypto/pbkdf2": found meta tag get.metaImport{Prefix:"golang.org/x/crypto", VCS:"git", RepoRoot:"https://go.googlesource.com/crypto"} at https://golang.org/x/crypto/pbkdf2?go-get=1
    get "golang.org/x/crypto/pbkdf2": verifying non-authoritative meta tag
    Fetching https://golang.org/x/crypto?go-get=1
    Parsing meta tags from https://golang.org/x/crypto?go-get=1 (status code 200)
    golang.org/x/crypto (download)
    Fetching https://gopkg.in/ini.v1?go-get=1
    Parsing meta tags from https://gopkg.in/ini.v1?go-get=1 (status code 200)
    get "gopkg.in/ini.v1": found meta tag get.metaImport{Prefix:"gopkg.in/ini.v1", VCS:"git", RepoRoot:"https://gopkg.in/ini.v1"} at https://gopkg.in/ini.v1?go-get=1
    gopkg.in/ini.v1 (download)
    github.com/go-martini/martini (download)
    github.com/codegangsta/inject (download)
    github.com/go-playground/lars (download)
    github.com/go-playground/form (download)
    github.com/gorilla/websocket (download)
    github.com/go-zoo/bone (download)
    github.com/gocraft/web (download)
    github.com/gorilla/mux (download)
    github.com/julienschmidt/httprouter (download)
    github.com/labstack/echo (download)
    github.com/labstack/gommon (download)
    github.com/mattn/go-colorable (download)
    github.com/valyala/fasttemplate (download)
    Fetching https://golang.org/x/crypto/acme/autocert?go-get=1
    Parsing meta tags from https://golang.org/x/crypto/acme/autocert?go-get=1 (status code 200)
    get "golang.org/x/crypto/acme/autocert": found meta tag get.metaImport{Prefix:"golang.org/x/crypto", VCS:"git", RepoRoot:"https://go.googlesource.com/crypto"} at https://golang.org/x/crypto/acme/autocert?go-get=1
    get "golang.org/x/crypto/acme/autocert": verifying non-authoritative meta tag
    Fetching https://golang.org/x/crypto/acme?go-get=1
    Parsing meta tags from https://golang.org/x/crypto/acme?go-get=1 (status code 200)
    get "golang.org/x/crypto/acme": found meta tag get.metaImport{Prefix:"golang.org/x/crypto", VCS:"git", RepoRoot:"https://go.googlesource.com/crypto"} at https://golang.org/x/crypto/acme?go-get=1
    get "golang.org/x/crypto/acme": verifying non-authoritative meta tag
    github.com/lunny/log (download)
    github.com/lunny/tango (download)
    github.com/mailgun/route (download)
    github.com/vulcand/predicate (download)
    github.com/gravitational/trace (download)
    github.com/jonboulle/clockwork (download)
    github.com/sirupsen/logrus (download)
    Fetching https://golang.org/x/crypto/ssh/terminal?go-get=1
    Parsing meta tags from https://golang.org/x/crypto/ssh/terminal?go-get=1 (status code 200)
    get "golang.org/x/crypto/ssh/terminal": found meta tag get.metaImport{Prefix:"golang.org/x/crypto", VCS:"git", RepoRoot:"https://go.googlesource.com/crypto"} at https://golang.org/x/crypto/ssh/terminal?go-get=1
    get "golang.org/x/crypto/ssh/terminal": verifying non-authoritative meta tag
    Fetching https://golang.org/x/sys/unix?go-get=1
    Parsing meta tags from https://golang.org/x/sys/unix?go-get=1 (status code 200)
    get "golang.org/x/sys/unix": found meta tag get.metaImport{Prefix:"golang.org/x/sys", VCS:"git", RepoRoot:"https://go.googlesource.com/sys"} at https://golang.org/x/sys/unix?go-get=1
    get "golang.org/x/sys/unix": verifying non-authoritative meta tag
    Fetching https://golang.org/x/sys?go-get=1
    Parsing meta tags from https://golang.org/x/sys?go-get=1 (status code 200)
    golang.org/x/sys (download)
    Fetching https://golang.org/x/net/context?go-get=1
    Parsing meta tags from https://golang.org/x/net/context?go-get=1 (status code 200)
    get "golang.org/x/net/context": found meta tag get.metaImport{Prefix:"golang.org/x/net", VCS:"git", RepoRoot:"https://go.googlesource.com/net"} at https://golang.org/x/net/context?go-get=1
    get "golang.org/x/net/context": verifying non-authoritative meta tag
    Fetching https://golang.org/x/net?go-get=1
    Parsing meta tags from https://golang.org/x/net?go-get=1 (status code 200)
    golang.org/x/net (download)
    github.com/mikespook/possum (download)
    Fetching https://gopkg.in/fsnotify.v1?go-get=1
    Parsing meta tags from https://gopkg.in/fsnotify.v1?go-get=1 (status code 200)
    get "gopkg.in/fsnotify.v1": found meta tag get.metaImport{Prefix:"gopkg.in/fsnotify.v1", VCS:"git", RepoRoot:"https://gopkg.in/fsnotify.v1"} at https://gopkg.in/fsnotify.v1?go-get=1
    gopkg.in/fsnotify.v1 (download)
    Fetching https://golang.org/x/net/websocket?go-get=1
    Parsing meta tags from https://golang.org/x/net/websocket?go-get=1 (status code 200)
    get "golang.org/x/net/websocket": found meta tag get.metaImport{Prefix:"golang.org/x/net", VCS:"git", RepoRoot:"https://go.googlesource.com/net"} at https://golang.org/x/net/websocket?go-get=1
    get "golang.org/x/net/websocket": verifying non-authoritative meta tag
    github.com/naoina/denco (download)
    github.com/naoina/kocha-urlrouter (download)
    github.com/pilu/traffic (download)
    github.com/pilu/config (download)
    github.com/plimble/ace (download)
    github.com/plimble/sessions (download)
    github.com/oxtoacart/bpool (download)
    github.com/satori/go.uuid (download)
    github.com/tinylib/msgp (download)
    github.com/philhofer/fwd (download)
    github.com/plimble/utils (download)
    github.com/rcrowley/go-tigertonic (download)
    github.com/rcrowley/go-metrics (download)
    github.com/revel/revel (download)
    github.com/revel/config (download)
    github.com/revel/log15 (download)
    github.com/inconshreveable/log15 (download)
    Fetching https://gopkg.in/stack.v0?go-get=1
    Parsing meta tags from https://gopkg.in/stack.v0?go-get=1 (status code 200)
    get "gopkg.in/stack.v0": found meta tag get.metaImport{Prefix:"gopkg.in/stack.v0", VCS:"git", RepoRoot:"https://gopkg.in/stack.v0"} at https://gopkg.in/stack.v0?go-get=1
    gopkg.in/stack.v0 (download)
    github.com/revel/pathtree (download)
    Fetching https://gopkg.in/natefinch/lumberjack.v2?go-get=1
    Parsing meta tags from https://gopkg.in/natefinch/lumberjack.v2?go-get=1 (status code 200)
    get "gopkg.in/natefinch/lumberjack.v2": found meta tag get.metaImport{Prefix:"gopkg.in/natefinch/lumberjack.v2", VCS:"git", RepoRoot:"https://gopkg.in/natefinch/lumberjack.v2"} at https://gopkg.in/natefinch/lumberjack.v2?go-get=1
    gopkg.in/natefinch/lumberjack.v2 (download)
    github.com/xeonx/timeago (download)
    Fetching https://gopkg.in/fsnotify/fsnotify.v1?go-get=1
    Parsing meta tags from https://gopkg.in/fsnotify/fsnotify.v1?go-get=1 (status code 200)
    get "gopkg.in/fsnotify/fsnotify.v1": found meta tag get.metaImport{Prefix:"gopkg.in/fsnotify/fsnotify.v1", VCS:"git", RepoRoot:"https://gopkg.in/fsnotify/fsnotify.v1"} at https://gopkg.in/fsnotify/fsnotify.v1?go-get=1
    gopkg.in/fsnotify/fsnotify.v1 (download)
    github.com/robfig/pathtree (download)
    github.com/typepress/rivet (download)
    github.com/ursiform/bear (download)
    github.com/vanng822/r2router (download)
    github.com/zenazn/goji (download)
    Fetching https://goji.io?go-get=1
    Parsing meta tags from https://goji.io?go-get=1 (status code 200)
    get "goji.io": found meta tag get.metaImport{Prefix:"goji.io", VCS:"git", RepoRoot:"https://github.com/goji/goji"} at https://goji.io?go-get=1
    goji.io (download)
    Fetching https://goji.io/internal?go-get=1
    Parsing meta tags from https://goji.io/internal?go-get=1 (status code 200)
    get "goji.io/internal": found meta tag get.metaImport{Prefix:"goji.io", VCS:"git", RepoRoot:"https://github.com/goji/goji"} at https://goji.io/internal?go-get=1
    get "goji.io/internal": verifying non-authoritative meta tag
    Fetching https://goji.io?go-get=1
    Parsing meta tags from https://goji.io?go-get=1 (status code 200)
    Fetching https://goji.io/pat?go-get=1
    Parsing meta tags from https://goji.io/pat?go-get=1 (status code 200)
    get "goji.io/pat": found meta tag get.metaImport{Prefix:"goji.io", VCS:"git", RepoRoot:"https://github.com/goji/goji"} at https://goji.io/pat?go-get=1
    get "goji.io/pat": verifying non-authoritative meta tag
    Fetching https://goji.io/pattern?go-get=1
    Parsing meta tags from https://goji.io/pattern?go-get=1 (status code 200)
    get "goji.io/pattern": found meta tag get.metaImport{Prefix:"goji.io", VCS:"git", RepoRoot:"https://github.com/goji/goji"} at https://goji.io/pattern?go-get=1
    get "goji.io/pattern": verifying non-authoritative meta tag
    github.com/julienschmidt/go-http-routing-benchmark
    # github.com/julienschmidt/go-http-routing-benchmark
    ./routers.go:1151:33: rc.Controller.Request.RequestURI undefined (type *revel.Request has no field or method RequestURI)
    ./routers.go:1167:27: cannot use r (type *http.Request) as type revel.ServerRequest in argument to revel.NewRequest:
    	*http.Request does not implement revel.ServerRequest (missing Get method)
    ./routers.go:1168:28: cannot use w (type http.ResponseWriter) as type revel.ServerResponse in argument to revel.NewResponse:
    	http.ResponseWriter does not implement revel.ServerResponse (missing Get method)
    ./routers.go:1169:30: too many arguments in call to revel.NewController
    	have (*revel.Request, *revel.Response)
    	want (revel.ServerContext)
    ./routers.go:1171:6: req.Websocket undefined (type *revel.Request has no field or method Websocket, but does have WebSocket)
    ./routers.go:1179:23: invalid type assertion: resp.Out.(io.Closer) (non-interface type revel.OutResponse on left)
    ./routers.go:1218:33: not enough arguments in call to revel.NewRoute
    	have (string, string, string, string, string, number)
    	want (*revel.Module, string, string, string, string, string, int)
    ./routers.go:1223:14: cannot use "github.com/robfig/pathtree".New() (type *"github.com/robfig/pathtree".Node) as type *"github.com/revel/pathtree".Node in assignment
    ./routers.go:1244:25: not enough arguments in call to revel.NewRoute
    	have (string, string, string, string, string, number)
    	want (*revel.Module, string, string, string, string, string, int)
    
  • Get the benchmark compiling / running again

    Get the benchmark compiling / running again

    • put dependencies under glide management, will make it easier to keep this benchmark functional.
    • disable benchmarking the echo framework
      • The Echo framework has changed a bunch of its interfaces, and no longer implements http.Handler in doing so. As such, Echo no longer works within this framework.
    • disable the rivet framework
      • In testing, the rivet framework was returning 200s but the body was always empty. This looks to be an issue within rivet and less to with the benchmark
  • Add goji v2 benchmark

    Add goji v2 benchmark

    Adding benchmarks for goji.io, the successor to github.com/zenazn/goji.

    Unfortunately everything is named Gojiv2 so you can no longer run only the Goji benchmark. Open to suggestions for better naming, or could go back and name Goji to Gojiv1 or similar.

  • add vanilla router to benchmark

    add vanilla router to benchmark

    Vanilla router aim is to serve http.Handler. It combines the following packages:

    It can be used as an example on how stable GO packages in the wild can be combined.

  • Testing of Zeus failed

    Testing of Zeus failed

    Running go test -bench=. -timeout=20m but failed:

    --- FAIL: TestRouters (2.72s)
        routers_test.go:78: Zeus in API Static: 301 - <a href="/articles">Moved Permanently</a>.
    
            ; expected GET /articles/
        routers_test.go:78: Zeus in API Static: 301 - <a href="/articles/wiki">Moved Permanently</a>.
    
            ; expected GET /articles/wiki/
        routers_test.go:78: Zeus in API Static: 301 - <a href="/codewalk">Moved Permanently</a>.
    
            ; expected GET /codewalk/
        routers_test.go:78: Zeus in API Static: 301 - <a href="/devel">Moved Permanently</a>.
    
            ; expected GET /devel/
        routers_test.go:78: Zeus in API Static: 301 - <a href="/gopher">Moved Permanently</a>.
    
            ; expected GET /gopher/
        routers_test.go:78: Zeus in API Static: 301 - <a href="/gopher/pencil">Moved Permanently</a>.
    
            ; expected GET /gopher/pencil/
        routers_test.go:78: Zeus in API Static: 301 - <a href="/play">Moved Permanently</a>.
    
            ; expected GET /play/
        routers_test.go:78: Zeus in API Static: 301 - <a href="/progs">Moved Permanently</a>.
    
            ; expected GET /progs/
    

    I'd like to make a PR, but I am not familiar with Zeus.

  • Request to add tested framework: gin

    Request to add tested framework: gin

    Gin is a web framework which features a Martini-like API with much better performance by using httprouter -- up to 40 times faster.

    https://gin-gonic.github.io/gin/

    Is it possible to include Gin into these tests? Thanks.

  • Adding go-resftul to benchmarks

    Adding go-resftul to benchmarks

    Hi Julien, I've added in go-resftul to the suite of benchmarks. go-restful does ok in some respects, but it comes in below average for RAM and speed. I'd say it's somewhat comparable to gorilla-mux, and a little better than Martini.

    I hope you find the pull-request in decent shape. I kept to the alphabetical-ish ordering, so goRestful ends up in between goJsonRest and gorillaMux. I ran go fmt on it aswell, so it should all be in order. I haven't updated the readme with results because I figure it'd be best to keep using the same hardware for those figures.

    As an aside, my interest in go-restful is that it's self-documenting and supports swagger. I'm deciding on which router to use, and your benchmark was really valuable in that assessment,

    My conclusion is that I'm interested in seeing/creating swagger support for httprouter, and then using httprouter. I've already made something ugly to acheive this using go-restful/swagger's code. I'm keen to fork it into something less hackish, but I'd like to see if you have ideas about it first. I'll post my ugly code as a gist and send you an issue on the httprouter project itself.

    Cheers, and thanks for all the good work on this stuff.

  • Bump github.com/labstack/echo/v4 from 4.1.11 to 4.9.0

    Bump github.com/labstack/echo/v4 from 4.1.11 to 4.9.0

    Bumps github.com/labstack/echo/v4 from 4.1.11 to 4.9.0.

    Release notes

    Sourced from github.com/labstack/echo/v4's releases.

    v4.9.0

    Security

    • Fix open redirect vulnerability in handlers serving static directories (e.Static, e.StaticFs, echo.StaticDirectoryHandler) #2260

    Enhancements

    • Allow configuring ErrorHandler in CSRF middleware #2257
    • Replace HTTP method constants in tests with stdlib constants #2247

    v4.8.0

    Most notable things

    You can now add any arbitrary HTTP method type as a route #2237

    e.Add("COPY", "/*", func(c echo.Context) error 
      return c.String(http.StatusOK, "OK COPY")
    })
    

    You can add custom 404 handler for specific paths #2217

    e.RouteNotFound("/*", func(c echo.Context) error { return c.NoContent(http.StatusNotFound) })
    

    g := e.Group("/images") g.RouteNotFound("/*", func(c echo.Context) error { return c.NoContent(http.StatusNotFound) })

    Enhancements

    • Add new value binding methods (UnixTimeMilli,TextUnmarshaler,JSONUnmarshaler) to Valuebinder #2127
    • Refactor: body_limit middleware unit test #2145
    • Refactor: Timeout mw: rework how test waits for timeout. #2187
    • BasicAuth middleware returns 500 InternalServerError on invalid base64 strings but should return 400 #2191
    • Refactor: duplicated findStaticChild process at findChildWithLabel #2176
    • Allow different param names in different methods with same path scheme #2209
    • Add support for registering handlers for different 404 routes #2217
    • Middlewares should use errors.As() instead of type assertion on HTTPError #2227
    • Allow arbitrary HTTP method types to be added as routes #2237

    v4.7.2

    Fixes

    • Fix nil pointer exception when calling Start again after address binding error #2131
    • Fix CSRF middleware not being able to extract token from multipart/form-data form #2136
    • Fix Timeout middleware write race #2126

    Enhancements

    ... (truncated)

    Changelog

    Sourced from github.com/labstack/echo/v4's changelog.

    v4.9.0 - 2022-09-04

    Security

    • Fix open redirect vulnerability in handlers serving static directories (e.Static, e.StaticFs, echo.StaticDirectoryHandler) #2260

    Enhancements

    • Allow configuring ErrorHandler in CSRF middleware #2257
    • Replace HTTP method constants in tests with stdlib constants #2247

    v4.8.0 - 2022-08-10

    Most notable things

    You can now add any arbitrary HTTP method type as a route #2237

    e.Add("COPY", "/*", func(c echo.Context) error 
      return c.String(http.StatusOK, "OK COPY")
    })
    

    You can add custom 404 handler for specific paths #2217

    e.RouteNotFound("/*", func(c echo.Context) error { return c.NoContent(http.StatusNotFound) })
    

    g := e.Group("/images") g.RouteNotFound("/*", func(c echo.Context) error { return c.NoContent(http.StatusNotFound) })

    Enhancements

    • Add new value binding methods (UnixTimeMilli,TextUnmarshaler,JSONUnmarshaler) to Valuebinder #2127
    • Refactor: body_limit middleware unit test #2145
    • Refactor: Timeout mw: rework how test waits for timeout. #2187
    • BasicAuth middleware returns 500 InternalServerError on invalid base64 strings but should return 400 #2191
    • Refactor: duplicated findStaticChild process at findChildWithLabel #2176
    • Allow different param names in different methods with same path scheme #2209
    • Add support for registering handlers for different 404 routes #2217
    • Middlewares should use errors.As() instead of type assertion on HTTPError #2227
    • Allow arbitrary HTTP method types to be added as routes #2237

    v4.7.2 - 2022-03-16

    Fixes

    • Fix nil pointer exception when calling Start again after address binding error #2131
    • Fix CSRF middleware not being able to extract token from multipart/form-data form #2136
    • Fix Timeout middleware write race #2126

    ... (truncated)

    Commits
    • 16d3b65 Changelog for 4.9.0
    • 0ac4d74 Fix #2259 open redirect vulnerability in echo.StaticDirectoryHandler (used by...
    • d77e8c0 Added ErrorHandler and ErrorHandlerWithContext in CSRF middleware (#2257)
    • 534bbb8 replace POST constance with stdlib constance
    • fb57d96 replace GET constance with stdlib constance
    • d48197d Changelog for 4.8.0
    • cba12a5 Allow arbitrary HTTP method types to be added as routes
    • a327884 add:README.md-Third-party middlewares-github.com/go-woo/protoc-gen-echo
    • 61422dd Update CI-flow (Go 1.19 +deps)
    • a9879ff Middlewares should use errors.As() instead of type assertion on HTTPError
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

  • Bump github.com/gin-gonic/gin from 1.5.0 to 1.7.0

    Bump github.com/gin-gonic/gin from 1.5.0 to 1.7.0

    Bumps github.com/gin-gonic/gin from 1.5.0 to 1.7.0.

    Release notes

    Sourced from github.com/gin-gonic/gin's releases.

    Release v1.7.0

    BUGFIXES

    • fix compile error from #2572 (#2600)
    • fix: print headers without Authorization header on broken pipe (#2528)
    • fix(tree): reassign fullpath when register new node (#2366)

    ENHANCEMENTS

    • Support params and exact routes without creating conflicts (#2663)
    • chore: improve render string performance (#2365)
    • Sync route tree to httprouter latest code (#2368)
    • chore: rename getQueryCache/getFormCache to initQueryCache/initFormCa (#2375)
    • chore(performance): improve countParams (#2378)
    • Remove some functions that have the same effect as the bytes package (#2387)
    • update:SetMode function (#2321)
    • remove a unused type SecureJSONPrefix (#2391)
    • Add a redirect sample for POST method (#2389)
    • Add CustomRecovery builtin middleware (#2322)
    • binding: avoid 2038 problem on 32-bit architectures (#2450)
    • Prevent panic in Context.GetQuery() when there is no Request (#2412)
    • Add GetUint and GetUint64 method on gin.context (#2487)
    • update content-disposition header to MIME-style (#2512)
    • reduce allocs and improve the render WriteString (#2508)
    • implement ".Unwrap() error" on Error type (#2525) (#2526)
    • Allow bind with a map[string]string (#2484)
    • chore: update tree (#2371)
    • Support binding for slice/array obj [Rewrite] (#2302)
    • basic auth: fix timing oracle (#2609)
    • Add mixed param and non-param paths (port of httprouter#329) (#2663)
    • feat(engine): add trustedproxies and remoteIP (#2632)

    Improve performance

    ENHANCEMENTS

    • Improve performance: Change *sync.RWMutex to sync.RWMutex in context. #2351

    release v1.6.2

    Release Notes

    • BUGFIXES
      • fix missing initial sync.RWMutex (#2305)
    • ENHANCEMENTS
      • Add set samesite in cookie. (#2306)

    Contributors

    release v1.6.1

    ... (truncated)

    Changelog

    Sourced from github.com/gin-gonic/gin's changelog.

    Gin v1.7.0

    BUGFIXES

    • fix compile error from #2572 (#2600)
    • fix: print headers without Authorization header on broken pipe (#2528)
    • fix(tree): reassign fullpath when register new node (#2366)

    ENHANCEMENTS

    • Support params and exact routes without creating conflicts (#2663)
    • chore: improve render string performance (#2365)
    • Sync route tree to httprouter latest code (#2368)
    • chore: rename getQueryCache/getFormCache to initQueryCache/initFormCa (#2375)
    • chore(performance): improve countParams (#2378)
    • Remove some functions that have the same effect as the bytes package (#2387)
    • update:SetMode function (#2321)
    • remove a unused type SecureJSONPrefix (#2391)
    • Add a redirect sample for POST method (#2389)
    • Add CustomRecovery builtin middleware (#2322)
    • binding: avoid 2038 problem on 32-bit architectures (#2450)
    • Prevent panic in Context.GetQuery() when there is no Request (#2412)
    • Add GetUint and GetUint64 method on gin.context (#2487)
    • update content-disposition header to MIME-style (#2512)
    • reduce allocs and improve the render WriteString (#2508)
    • implement ".Unwrap() error" on Error type (#2525) (#2526)
    • Allow bind with a map[string]string (#2484)
    • chore: update tree (#2371)
    • Support binding for slice/array obj [Rewrite] (#2302)
    • basic auth: fix timing oracle (#2609)
    • Add mixed param and non-param paths (port of httprouter#329) (#2663)
    • feat(engine): add trustedproxies and remoteIP (#2632)

    Gin v1.6.3

    ENHANCEMENTS

    • Improve performance: Change *sync.RWMutex to sync.RWMutex in context. #2351

    Gin v1.6.2

    BUGFIXES

    • fix missing initial sync.RWMutex #2305

    ENHANCEMENTS

    • Add set samesite in cookie. #2306

    Gin v1.6.1

    BUGFIXES

    • Revert "fix accept incoming network connections" #2294

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

  • Bump gopkg.in/macaron.v1 from 1.3.4 to 1.3.7

    Bump gopkg.in/macaron.v1 from 1.3.4 to 1.3.7

    Bumps gopkg.in/macaron.v1 from 1.3.4 to 1.3.7.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

  • add lenrouter

    add lenrouter

    I would like to add a router I wrote to the benchmarks. It uses length of the path to make the routing decisions.

    https://github.com/PratikDeoghare/lenrouter

    Please, take a look. Danke!

A rapid http(s) benchmark tool written in Go
A rapid http(s) benchmark tool written in Go

gonetx/httpit httpit is a rapid http(s) benchmark tool which on top of fasthttp. Also thanks to cobra and bubbletea. Installation Get binaries from re

Dec 23, 2022
Go http server benchmark
Go http server benchmark

go-http-server-benchmark The more connections, nbio cost the less memory, and performance the better than other frameworks. We can serve for 1000k or

Sep 1, 2022
Go benchmark harness.

autobench autobench is a framework to compare the performance of Go 1.2 and Go 1.3. usage autobench downloads and builds the latest Go 1.1 and Go tip

Oct 19, 2022
Key/Value database benchmark

USAGE: kvbench OPTIONS OVERVIEW kvbench is a simple benchmarking tool to evaluate the read performance of a key/value store while writes are being app

Sep 27, 2022
Cache benchmark for Golang
Cache benchmark for Golang

Cache comparison benchmark for Go This benchmark compares cache algorithms using scrambled zipfian distribution (a few occur very often while many oth

Dec 26, 2022
Golang json encoders\decoders benchmark

Json encoder and decoder benchmark Test Encode for: Build-in encoder https://github.com/mailru/easyjson https://github.com/json-iterator/go Test Decod

Dec 3, 2021
Go Script Lang Benchmark

Go Script Lang Benchmark git clone github.com/akkuman/go_script_lang_benchmark c

Nov 9, 2022
Application for HTTP benchmarking via different rules and configs
Application for HTTP benchmarking via different rules and configs

Go Benchmark App The efficiency and speed of application - our goal and the basic idea. Application for HTTP-benchmarking via different rules and conf

Dec 24, 2022
Performance-focused HTTP benchmarking tool written in Go

?? gocannon - HTTP benchmarking tool Gocannon is a lightweight HTTP benchmarking tool, intended to measure changes in backend application performance

Dec 22, 2022
Go HTTP request router and web framework benchmark

Go HTTP Router Benchmark This benchmark suite aims to compare the performance of HTTP request routers for Go by implementing the routing structure of

Dec 27, 2022
Bxog is a simple and fast HTTP router for Go (HTTP request multiplexer).

Bxog is a simple and fast HTTP router for Go (HTTP request multiplexer). Usage An example of using the multiplexer: package main import ( "io" "net

Dec 26, 2022
Go Server/API micro framework, HTTP request router, multiplexer, mux
Go Server/API micro framework, HTTP request router, multiplexer, mux

?? gorouter Go Server/API micro framework, HTTP request router, multiplexer, mux. ?? ABOUT Contributors: Rafał Lorenz Want to contribute ? Feel free t

Dec 16, 2022
Redis-benchmark - Simple get, mget and pipelined get benchmark.

redis-benchmark Simple get, mget and pipelined get benchmark. Usage git clone https://github.com/Ali-A-A/redis-benchmark.git cd ./redis-benchmark dock

Dec 31, 2021
Bxd redis benchmark - Redis benchmark tool for golang

使用 redis benchmark 工具, 测试 10 20 50 100 200 1k 5k 字节 value 大小,redis get set 性能。 r

Jan 22, 2022
Go-driver-benchmark - Driver benchmark with golang

We use ClickkHouse for time-series databases, and the driver's performance is ve

Sep 5, 2022
Benchmark - Benchmark of logr implementations

Benchmark of logr implementations Implementations a function (can bridge to non-

Nov 6, 2022
A benchmark suite aims to compare the performance of HTTP request routers for Go

Go HTTP Router Benchmark This benchmark suite aims to compare the performance of HTTP request routers for Go by implementing the routing structure of

Oct 25, 2021
An HTTP request routing benchmark suite for Go.

Go HTTP Request Routing Benchmark An HTTP request routing benchmark suite for Go. Results goos: linux goarch: amd64 pkg: github.com/aofei/go-http-requ

Dec 14, 2021
xujiajun/gorouter is a simple and fast HTTP router for Go. It is easy to build RESTful APIs and your web framework.

gorouter xujiajun/gorouter is a simple and fast HTTP router for Go. It is easy to build RESTful APIs and your web framework. Motivation I wanted a sim

Dec 8, 2022
Dec 28, 2022