When building web applications or APIs in Go, choosing the right HTTP router can significantly impact performance, especially at scale. This article analyzes and compares the performance characteristics of popular Go HTTP routers to help you make an informed choice for your next project.

Introduction

The HTTP router is a critical component in any web application, responsible for matching incoming requests to the appropriate handlers. In Go’s ecosystem, developers have numerous router options ranging from the standard library’s http.ServeMux to feature-rich third-party packages.

While functionality and developer experience are important factors when selecting a router, performance characteristics become increasingly critical as applications scale. This is particularly true for high-traffic services or resource-constrained environments.

This article presents benchmark results comparing 16 popular Go HTTP routers, analyzing their performance across different routing scenarios.

Routers Examined

Our benchmarks include the following HTTP routers:

  1. Standard Library: net/http.ServeMux - Go’s built-in HTTP router
  2. github.com/bmf-san/goblin - A simple trie-based HTTP router
  3. github.com/julienschmidt/httprouter - A popular high-performance router based on a radix tree
  4. github.com/go-chi/chi - A lightweight, composable router with middleware support
  5. github.com/gin-gonic/gin - A full-featured web framework with a router at its core
  6. github.com/uptrace/bunrouter - A fast and flexible HTTP router
  7. github.com/dimfeld/httptreemux - An adaptation of httprouter with more flexible parameter handling
  8. github.com/beego/mux - The router component from the Beego framework
  9. github.com/gorilla/mux - A powerful URL router and dispatcher with extensive pattern matching
  10. github.com/nissy/bon - A lightweight, fast HTTP router
  11. github.com/naoina/denco - An HTTP router that uses a double-array trie
  12. github.com/labstack/echo - A high-performance, minimalist framework
  13. github.com/gocraft/web - A router focused on middleware and context
  14. github.com/vardius/gorouter - A CORS-supporting HTTP router
  15. github.com/go-ozzo/ozzo-routing - A fast routing engine from the Ozzo framework
  16. github.com/lkeix/techbook13-sample - A sample router implementation

Benchmark Methodology

Test Environment

All benchmarks were run on the following environment:

  • Go Version: 1.19
  • Operating System: Darwin (macOS)
  • Architecture: amd64
  • CPU: VirtualApple @ 2.50GHz

Test Cases

Our benchmarks focus on two primary routing scenarios:

  1. Static Routes: Fixed URL paths without variables
  2. Path Parameter Routes: URLs containing path parameters (e.g., /user/:id)

Static Routes Tests

For static routes, we tested four different path patterns:

  • Root: /
  • Simple: /foo
  • Medium: /foo/bar/baz/qux/quux
  • Long: /foo/bar/baz/qux/quux/corge/grault/garply/waldo/fred

These tests evaluate how routers handle paths of increasing length and complexity.

Path Parameter Tests

For routes with path parameters, we tested three patterns with increasing numbers of parameters:

  • Single parameter: /foo/:bar
  • Five parameters: /foo/:bar/:baz/:qux/:quux/:corge
  • Ten parameters: /foo/:bar/:baz/:qux/:quux/:corge/:grault/:garply/:waldo/:fred/:plugh

Since router implementations may use different syntax for path parameters (e.g., :param, {param}, or <param>), the tests account for these differences.

Metrics Measured

For each test case, we measured four key performance metrics:

  1. Execution Count (time): Number of function executions completed during the benchmark period. Higher is better.
  2. Time per Operation (ns/op): Average time in nanoseconds required per function execution. Lower is better.
  3. Memory Allocation (B/op): Average memory allocated in bytes per operation. Lower is better.
  4. Allocation Count (allocs/op): Average number of heap allocations per operation. Lower is better.

Benchmark Results

Static Routes Results

Execution Count (time)

RouterRoot PathSimple PathMedium PathLong Path
servemux24,301,91022,053,46813,324,3578,851,803
goblin32,296,87916,738,8135,753,0883,111,172
httprouter100,000,000100,000,000100,000,00072,498,970
chi5,396,6525,350,2855,353,8565,415,325
gin34,933,86134,088,81034,136,85233,966,028
bunrouter63,478,48654,812,66553,564,05554,345,159
httptreemux6,669,2316,219,1575,278,3124,300,488
beegomux22,320,19915,369,3201,000,000577,272
gorillamux1,807,0422,104,2101,904,6961,869,037
bon72,425,13256,830,17759,573,30558,364,338
denco90,249,31392,561,34489,325,31273,905,086
echo41,742,09336,207,87823,962,47812,379,764
gocraftweb1,284,6131,262,8631,000,000889,360
gorouter21,622,92028,592,13415,582,7789,636,147
ozzorouting31,406,93134,989,97024,825,55219,431,296
techbook13-sample8,176,8496,349,8962,684,4181,384,840

Time per Operation (ns/op)

RouterRoot PathSimple PathMedium PathLong Path
servemux50.4454.9789.81135.2
goblin36.6369.9205.2382.7
httprouter10.6510.7410.7516.42
chi217.2220.1216.7221.5
gin34.5334.9134.6935.04
bunrouter18.7721.7822.4122.0
httptreemux178.8190.9227.2277.7
beegomux55.0774.6910802046
gorillamux595.7572.8626.5643.3
bon15.7520.1718.8719.16
denco14.013.0313.415.87
echo28.1732.8349.8296.77
gocraftweb929.4948.810781215
gorouter55.1637.6476.6124.1
ozzorouting42.6234.2248.1261.6
techbook13-sample146.1188.4443.5867.8

Memory Allocation (B/op)

RouterRoot PathSimple PathMedium PathLong Path
servemux0000
goblin01680160
httprouter0000
chi304304304304
gin0000
bunrouter0000
httptreemux328328328328
beegomux32323232
gorillamux720720720720
bon0000
denco0000
echo0000
gocraftweb288288352432
gorouter0000
ozzorouting0000
techbook13-sample304308432872

Allocation Count (allocs/op)

RouterRoot PathSimple PathMedium PathLong Path
servemux0000
goblin0111
httprouter0000
chi2222
gin0000
bunrouter0000
httptreemux3333
beegomux1111
gorillamux7777
bon0000
denco0000
echo0000
gocraftweb6666
gorouter0000
ozzorouting0000
techbook13-sample231121

Path Parameter Routes Results

Execution Count (time)

Router1 Parameter5 Parameters10 Parameters
goblin1,802,690492,392252,274
httprouter25,775,94010,057,8746,060,843
chi4,337,9222,687,1571,772,881
gin29,479,38115,714,6739,586,220
bunrouter37,098,7728,479,6423,747,968
httptreemux2,610,3241,550,306706,356
beegomux3,177,818797,472343,969
gorillamux1,364,386470,180223,627
bon6,639,2164,486,7803,285,571
denco20,093,1678,503,3174,988,640
echo30,667,13712,028,7136,721,176
gocraftweb921,375734,821466,641
gorouter4,678,6173,038,4502,136,946
ozzorouting27,126,00012,228,0377,923,040
techbook13-sample3,019,774917,042522,897

Time per Operation (ns/op)

Router1 Parameter5 Parameters10 Parameters
goblin652.42,3414,504
httprouter45.73117.4204.2
chi276.4442.8677.6
gin40.2176.39124.3
bunrouter32.52141.1317.2
httptreemux399.7778.51,518
beegomux377.21,4463,398
gorillamux850.32,4235,264
bon186.5269.6364.4
denco60.47139.4238.7
echo39.3699.6175.7
gocraftweb1,1811,5402,280
gorouter256.4393557.6
ozzorouting43.6699.52150.4
techbook13-sample380.71,1542,150

Memory Allocation (B/op)

Router1 Parameter5 Parameters10 Parameters
goblin4099621,608
httprouter32160320
chi304304304
gin000
bunrouter000
httptreemux6809041,742
beegomux6726721,254
gorillamux1,0241,0881,751
bon304304304
denco32160320
echo000
gocraftweb6569441,862
gorouter360488648
ozzorouting000
techbook13-sample4329681,792

Allocation Count (allocs/op)

Router1 Parameter5 Parameters10 Parameters
goblin61319
httprouter111
chi222
gin000
bunrouter000
httptreemux6911
beegomux556
gorillamux889
bon222
denco111
echo000
gocraftweb91214
gorouter444
ozzorouting000
techbook13-sample103359

Analysis and Insights

Static Routes Performance

For static routes, several routers showed exceptional performance:

  1. httprouter consistently delivers the best performance across all path lengths. Its execution count is significantly higher than other routers, and its time per operation is the lowest.

  2. denco and bon also demonstrate excellent performance characteristics, often within 1.5-2x of httprouter’s speed.

  3. gin and bunrouter form the next performance tier, delivering solid results and maintaining consistent performance as path complexity increases.

  4. gorillamux and gocraftweb show the least favorable performance for static routes, with the highest time per operation and lowest execution counts.

An interesting observation is how some routers (like beegomux) show significant performance degradation as path length increases, while others (like httprouter, gin, and bunrouter) maintain consistent performance regardless of path complexity.

Path Parameter Performance

When handling routes with path parameters, the performance landscape changes:

  1. bunrouter, gin, and echo show excellent performance for single parameter routes, but their performance decreases as parameter count increases.

  2. httprouter maintains a strong balance of performance across all parameter counts, showing good scalability.

  3. denco and ozzorouting also perform well across the parameter count spectrum.

  4. gorillamux and goblin show the most significant performance degradation as parameter count increases.

In most routers, there’s a clear inverse relationship between the number of path parameters and performance. This correlation is expected, as more parameters require more string parsing, variable extraction, and context storing.

Memory Efficiency

Memory efficiency is particularly important in high-throughput applications where router operations occur thousands of times per second:

  1. gin, bunrouter, echo, and ozzorouting stand out by achieving zero allocations per operation in all test cases, indicating highly optimized memory usage.

  2. httprouter and denco show minimal allocations only for path parameter routes, but none for static routes.

  3. gorillamux consistently shows the highest memory allocation and allocation count across all tests, which may impact performance in memory-constrained environments.

The correlation between low memory allocation and high performance is evident in the results, with the best-performing routers generally having fewer allocations.

Data Structure Impact

The internal data structures used by these routers significantly influence their performance:

  1. Radix Tree (Patricia Trie): Used by httprouter, gin, echo, chi, and bon. This structure optimizes path matching by sharing common prefixes, resulting in efficient memory usage and fast lookups. The strong performance of these routers demonstrates the effectiveness of this data structure for HTTP routing.

  2. Double Array Trie: Implemented by denco, offering compact storage and fast lookups, particularly beneficial for static routes.

  3. Standard Trie: Used by goblin, which shows reasonable performance for simple cases but degrades with complexity.

  4. Linear Matching: Used by the standard library’s ServeMux, which is surprisingly competitive for simple static routes but lacks support for path parameters.

  5. RegExp-based Matching: Some routers use regular expressions for complex patterns, which typically shows poorer performance characteristics.

Choosing the Right Router for Your Application

Based on the benchmark results, here are recommendations for different application scenarios:

High-Performance APIs

For applications where raw routing performance is critical:

  1. httprouter offers the best overall performance across both static and parameter-based routes
  2. gin provides excellent performance with additional features and zero memory allocations
  3. bunrouter is a strong contender, especially for applications with fewer path parameters

Feature-Rich Applications

If you need more functionality beyond basic routing:

  1. echo balances good performance with a comprehensive feature set
  2. chi offers a middleware-focused approach with reasonable performance
  3. gin combines high performance with extensive feature set

Memory-Constrained Environments

For applications running in environments with limited memory:

  1. bunrouter, gin, and echo stand out with zero allocations
  2. httprouter and denco are also excellent choices with minimal allocations
  3. Avoid gorillamux and gocraftweb which have higher memory footprints

Simple Applications

For simpler applications with basic routing needs:

  1. The standard library’s ServeMux performs surprisingly well for static routes
  2. goblin provides a lightweight alternative with path parameter support
  3. bon offers good performance with minimal features

Performance Optimization Strategies

If you’re implementing your own HTTP router or optimizing an existing application, consider these strategies from the best-performing routers:

  1. Efficient Data Structures: Utilize specialized tree structures like radix trees or double-array tries that optimize for common prefixes in URL paths.

  2. Minimize Allocations: Reduce or eliminate memory allocations in the hot path. Pre-allocate data structures where possible and reuse them across requests.

  3. Avoid Regular Expressions: While flexible, regex-based matching tends to be slower than specialized path matching algorithms.

  4. Use Fixed-Size Data Structures: When parameters are known in advance, use fixed-size arrays instead of dynamic slices to avoid allocations.

  5. Path Normalization: Handle path normalization (like trailing slashes or multiple slashes) during route registration rather than at request time.

  6. Parameter Extraction Optimization: Extract path parameters efficiently, preferably without string allocations.

Conclusion

The performance characteristics of HTTP routers in Go vary significantly, with trade-offs between speed, memory efficiency, and features. The benchmarks reveal that:

  1. The best-performing routers leverage optimized data structures like radix trees or double-array tries.
  2. Performance tends to correlate inversely with memory allocations.
  3. There’s often a trade-off between feature richness and raw performance.
  4. Some routers maintain consistent performance across different routing scenarios, while others show significant degradation as complexity increases.

While these benchmarks provide valuable insights, it’s important to remember that router performance is just one aspect of overall application performance. In practice, database queries, business logic, and external service calls typically dominate the request processing time.

For most applications, selecting a router with good developer ergonomics that meets your feature requirements will likely be more beneficial than choosing solely based on benchmark results. However, for high-volume services where router performance could become a bottleneck, these benchmarks offer valuable guidance.

Remember that the best router for your application depends on your specific requirements, including performance needs, feature requirements, and development preferences.

References