Compare the performance of routers built with golang.
- go-router-benchmark
- Table of contents
- Listed routers
- Motivation
- Benchmark test
- Run benchmark tests
- Results
- Conclusion
- Contribution
- Contribution
- Sponsor
- License
- bmf-san/goblin
- julienschmidt/httprouter
- go-chi/chi
- gin-gonic/gin
- uptrace/bunrouter
- dimfeld/httptreemux
- beego/mux
- gorilla/mux
- nissy/bon
- naoina/denco
- labstack/echo
- gocraft/web
- vardius/gorouter
- go-ozzo/ozzo-routing
- lkeix/techbook13-sample
Since net/http#ServeMux does not have the capability to support path param route, only the static route test case is comparable.
I have implemented a router called bmf-san/goblin, and I created this repository to compare it with other routers and get hints on how to improve bmf-san/goblin.
Another reason is that julienschmidt/go-http-routing-benchmark seems to have stopped being maintained, so I wanted to have a benchmarker for the router that I could manage myself.
This benchmark tests is not a perfect comparison of HTTP Router performance. The reasons are as follows
- Not practical to cover all test cases because each HTTP Router has different specifications
- Performance may be unfairly evaluated depending on the routing test cases defined, since each HTTP Router has its own strengths and weaknesses depending on its data structures and algorithms.
Although the benchmark test is based on a specific case, it is possible to see some trends in performance differences.
Performance measurements will be made on the routing process of the HTTP Router.
More specifically, we will test the ServeHTTP
function of http#Handler.
We do not measure the performance of the process that defines the routing of the HTTP Router. The process of defining routing is the process of registering data for the routing process. For example, the code for net/http is as follows.
package main
import (
"fmt"
"net/http" )
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World")
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", handler) // here
ListenAndServe(":8080", mux)
}
We believe that the part that handles routing is called more often than the part that defines routing, and thus accounts for the majority of the HTTP Router's performance.
A static route is a route without variable parameters such as /foo/bar
.
Static route measures performance by benchmarking routing tests with the following three input values.
/
/foo
/foo/bar/baz/qux/quux
/foo/bar/baz/qux/quux/corge/grault/garply/waldo/fred
A path parameter route is a route with variable parameters such as /foo/:bar
.
In path parameter route, we perform benchmark routing tests with the following three input values to measure the performance.
/foo/:bar
/foo/:bar/:baz/:qux/:quux/:corge
/foo/:bar/:baz/:qux/:quux/:corge/:grault/:garply/:waldo/:fred/:plugh
Since different HTTP Routers have different ways of expressing parameters, there are several cases where another symbol is used in addition to :
.
Command | Description |
---|---|
test-benchmark | Run benchmark tests. |
test-benchmark-static | Run benchmark tests only static. |
test-benchmark-pathparam | Run benchmark tests only pathparam. |
test-benchmark-by-regexp | Run benchmark tests using regexp. ex. make test-benchmark-by-regexp EXP=Goblin, make test-benchmark-by-name EXP=StaticRoutes1 |
Benchmark results are published in a spreadsheet.
Benchmark system
- go version: go1.22
- goos: darwin
- goarch: amd64
- pkg: github.com/go-router-benchmark
- cpu: VirtualApple @ 2.50GHz
time | static-routes-root | static-routes-1 | static-routes-5 | static-routes-10 |
---|---|---|---|---|
servemux | 27831720 | 19891950 | 4838466 | 2508039 |
goblin | 78151288 | 39773182 | 10053289 | 4709226 |
httprouter | 144303050 | 134171820 | 129710535 | 100000000 |
chi | 7614604 | 7730403 | 7558288 | 6086882 |
gin | 44050909 | 43401141 | 43176404 | 41443738 |
bunrouter | 100000000 | 79699796 | 76368669 | 71506602 |
httptreemux | 8800170 | 8486769 | 6520071 | 4769065 |
beegomux | 31233393 | 26120126 | 8418674 | 4674050 |
gorillamux | 3210363 | 3138418 | 2914868 | 2262861 |
bon | 100000000 | 100000000 | 100000000 | 97284812 |
denco | 134409880 | 134490919 | 133387552 | 131848746 |
echo | 65374460 | 60613204 | 39926578 | 23414919 |
gocraftweb | 1686422 | 1644936 | 1460192 | 1208860 |
gorouter | 89735841 | 64869247 | 31833826 | 18472964 |
ozzorouting | 47574130 | 45542164 | 35507553 | 27385978 |
techbook13-sample | 10733499 | 7743865 | 3166988 | 1523804 |
nsop | static-routes-root | static-routes-1 | static-routes-5 | static-routes-10 |
---|---|---|---|---|
servemux | 43.09 | 60.18 | 226.1 | 457.7 |
goblin | 15.34 | 30.23 | 119.7 | 265.5 |
httprouter | 8.328 | 8.931 | 9.291 | 10.58 |
chi | 160 | 159.4 | 159.5 | 183.6 |
gin | 27.66 | 27.68 | 27.81 | 29.56 |
bunrouter | 10.63 | 15.24 | 15.72 | 18.61 |
httptreemux | 137.1 | 140.7 | 183.2 | 270.6 |
beegomux | 38.22 | 46.08 | 143.6 | 251.2 |
gorillamux | 374.9 | 380.8 | 411.7 | 550.8 |
bon | 10.68 | 10.46 | 10.7 | 10.93 |
denco | 9.027 | 8.986 | 8.962 | 9.076 |
echo | 18.54 | 19.73 | 30.14 | 50.64 |
gocraftweb | 709.5 | 718 | 826 | 1024 |
gorouter | 13.7 | 18.46 | 37.49 | 71.49 |
ozzorouting | 25.4 | 26.31 | 33.01 | 43.9 |
techbook13-sample | 115.3 | 154.8 | 379.8 | 779.6 |
bop | static-routes-root | static-routes-1 | static-routes-5 | static-routes-10 |
---|---|---|---|---|
servemux | 0 | 0 | 0 | 0 |
goblin | 0 | 0 | 0 | 0 |
httprouter | 0 | 0 | 0 | 0 |
chi | 336 | 336 | 336 | 336 |
gin | 0 | 0 | 0 | 0 |
bunrouter | 0 | 0 | 0 | 0 |
httptreemux | 360 | 360 | 360 | 360 |
beegomux | 32 | 32 | 32 | 32 |
gorillamux | 784 | 784 | 784 | 784 |
bon | 0 | 0 | 0 | 0 |
denco | 0 | 0 | 0 | 0 |
echo | 0 | 0 | 0 | 0 |
gocraftweb | 288 | 288 | 352 | 432 |
gorouter | 0 | 0 | 0 | 0 |
ozzorouting | 0 | 0 | 0 | 0 |
techbook13-sample | 336 | 340 | 464 | 904 |
allocs | static-routes-root | static-routes-1 | static-routes-5 | static-routes-10 |
---|---|---|---|---|
servemux | 0 | 0 | 0 | 0 |
goblin | 0 | 0 | 0 | 0 |
httprouter | 0 | 0 | 0 | 0 |
chi | 2 | 2 | 2 | 2 |
gin | 0 | 0 | 0 | 0 |
bunrouter | 0 | 0 | 0 | 0 |
httptreemux | 3 | 3 | 3 | 3 |
beegomux | 1 | 1 | 1 | 1 |
gorillamux | 7 | 7 | 7 | 7 |
bon | 0 | 0 | 0 | 0 |
denco | 0 | 0 | 0 | 0 |
echo | 0 | 0 | 0 | 0 |
gocraftweb | 6 | 6 | 6 | 6 |
gorouter | 0 | 0 | 0 | 0 |
ozzorouting | 0 | 0 | 0 | 0 |
techbook13-sample | 2 | 3 | 11 | 21 |
time | pathparam-routes-1 | pathparam-routes-5 | pathparam-routes-10 |
---|---|---|---|
servemux | 9879615 | 3095632 | 1716556 |
goblin | 5543427 | 3288781 | 2148358 |
httprouter | 33200109 | 12520192 | 7242321 |
chi | 4124253 | 2560972 | 1262286 |
gin | 35934104 | 18361316 | 10710009 |
bunrouter | 59392094 | 10587997 | 5214822 |
httptreemux | 3764035 | 2022978 | 1000000 |
beegomux | 3953678 | 2586265 | 1457932 |
gorillamux | 2004242 | 961486 | 521152 |
bon | 7565557 | 5322081 | 4093870 |
denco | 24679846 | 9708924 | 6409720 |
echo | 46360610 | 15237498 | 8387311 |
gocraftweb | 1287634 | 960009 | 717404 |
gorouter | 6209989 | 4295180 | 3322506 |
ozzorouting | 35132318 | 15635671 | 9333058 |
techbook13-sample | 3436351 | 1000000 | 647994 |
nsop | pathparam-routes-1 | pathparam-routes-5 | pathparam-routes-10 |
---|---|---|---|
servemux | 120.8 | 383.8 | 697.1 |
goblin | 217.7 | 367.2 | 558 |
httprouter | 36.3 | 96.27 | 155.5 |
chi | 304.8 | 468.1 | 961.9 |
gin | 33.44 | 64.27 | 104.1 |
bunrouter | 20.3 | 115.1 | 229.9 |
httptreemux | 316.6 | 599 | 1080 |
beegomux | 316.7 | 466.9 | 826 |
gorillamux | 609.5 | 1215 | 2327 |
bon | 159.2 | 226.9 | 293.9 |
denco | 49.59 | 123.2 | 187 |
echo | 26.17 | 78.84 | 143.4 |
gocraftweb | 936.6 | 1228 | 1654 |
gorouter | 196.1 | 277.9 | 364.7 |
ozzorouting | 33.99 | 77.03 | 129 |
techbook13-sample | 355.3 | 1045 | 1813 |
bop | pathparam-routes-1 | pathparam-routes-5 | pathparam-routes-10 |
---|---|---|---|
servemux | 16 | 240 | 496 |
goblin | 360 | 360 | 360 |
httprouter | 32 | 160 | 320 |
chi | 672 | 672 | 1254 |
gin | 0 | 0 | 0 |
bunrouter | 0 | 0 | 0 |
httptreemux | 712 | 936 | 1774 |
beegomux | 704 | 704 | 1286 |
gorillamux | 1088 | 1152 | 1815 |
bon | 336 | 336 | 336 |
denco | 32 | 160 | 320 |
echo | 0 | 0 | 0 |
gocraftweb | 656 | 944 | 1862 |
gorouter | 392 | 520 | 680 |
ozzorouting | 0 | 0 | 0 |
techbook13-sample | 464 | 1000 | 1824 |
allocs | pathparam-routes-1 | pathparam-routes-5 | pathparam-routes-10 |
---|---|---|---|
servemux | 1 | 4 | 5 |
goblin | 3 | 3 | 3 |
httprouter | 1 | 1 | 1 |
chi | 4 | 4 | 5 |
gin | 0 | 0 | 0 |
bunrouter | 0 | 0 | 0 |
httptreemux | 6 | 9 | 11 |
beegomux | 5 | 5 | 6 |
gorillamux | 8 | 8 | 9 |
bon | 2 | 2 | 2 |
denco | 1 | 1 | 1 |
echo | 0 | 0 | 0 |
gocraftweb | 9 | 12 | 14 |
gorouter | 4 | 4 | 4 |
ozzorouting | 0 | 0 | 0 |
techbook13-sample | 10 | 33 | 59 |
It can be seen that the HTTP Router with better performance has less performance degradation in each test case. This is a clear trend that shows that the implementation is optimized.
The better performing HTTP Route seems to employ a more sophisticated tree structure. Echo,gin,httprouter,bon,chi seem to adopt Radix tree (Patricia trie) and denco double array.
As for my implementation of goblin, it is a proprietary extension of the trie tree, which is not very well optimized, and I could clearly see that its performance is lower than the other HTTP Routers. (I will try my best to improve it...)
I don't think it is reasonable to assume that HTTP Router should not be adopted because of its seemingly poor performance in this benchmark result.
Some HTTP Routers are considered to be highly functional and easy to use, even if their performance is a little lower.
Some of the HTTP Routers listed here are no longer under development.
Since goblin has no plans to stop development for the time being, please try it if you like!
We are always accepting issues, pull requests, and other requests and questions.
We look forward to your contribution!
If you have an HTTP Router or test case you would like to add, please send us an Issue or Pull Request.
If you are submitting an Issue, please tell us about the HTTP Router you would like to add.
If you are submitting a PullRequest, please add a test case. Reporting of benchmark runs is done by the owner, so you do not need to update the run results.
If you have any questions, you can ask them in Issue.
Issues and Pull Requests are always welcome.
We would be happy to receive your contributions.
Please review the following documents before making a contribution.
If you like it, I would be happy to have you sponsor it!
Or I would be happy to get a STAR.
It motivates me to keep up with ongoing maintenance :D
Based on the MIT License.
- Blog