Skip to content

Commit 3a8edbc

Browse files
committed
Add Benchmarks
0 parents  commit 3a8edbc

File tree

6 files changed

+1345
-0
lines changed

6 files changed

+1345
-0
lines changed

LICENSE

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Copyright (c) 2013 Julien Schmidt. All rights reserved.
2+
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions are met:
6+
* Redistributions of source code must retain the above copyright
7+
notice, this list of conditions and the following disclaimer.
8+
* Redistributions in binary form must reproduce the above copyright
9+
notice, this list of conditions and the following disclaimer in the
10+
documentation and/or other materials provided with the distribution.
11+
* The names of the contributors may not be used to endorse or promote
12+
products derived from this software without specific prior written
13+
permission.
14+
15+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18+
DISCLAIMED. IN NO EVENT SHALL JULIEN SCHMIDT BE LIABLE FOR ANY
19+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

bench_test.go

+354
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
// Copyright 2013 Julien Schmidt. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be found
3+
// in the LICENSE file.
4+
5+
package benchmark
6+
7+
import (
8+
"github.com/bmizerany/pat"
9+
"github.com/codegangsta/martini"
10+
"github.com/gocraft/web"
11+
"github.com/gorilla/mux"
12+
"github.com/julienschmidt/httprouter"
13+
"github.com/pilu/traffic"
14+
"github.com/rcrowley/go-tigertonic"
15+
"io"
16+
"log"
17+
"net/http"
18+
"regexp"
19+
"testing"
20+
)
21+
22+
type route struct {
23+
method string
24+
path string
25+
}
26+
27+
type mockResponseWriter struct{}
28+
29+
func (m *mockResponseWriter) Header() (h http.Header) {
30+
return http.Header{}
31+
}
32+
33+
func (m *mockResponseWriter) Write(p []byte) (n int, err error) {
34+
return len(p), nil
35+
}
36+
37+
func (m *mockResponseWriter) WriteString(s string) (n int, err error) {
38+
return len(s), nil
39+
}
40+
41+
func (m *mockResponseWriter) WriteHeader(int) {}
42+
43+
func init() {
44+
log.SetOutput(new(mockResponseWriter))
45+
}
46+
47+
func benchRequest(b *testing.B, router http.Handler, r *http.Request) {
48+
w := new(mockResponseWriter)
49+
u := r.URL
50+
rq := u.RawQuery
51+
52+
b.ReportAllocs()
53+
b.ResetTimer()
54+
55+
for i := 0; i < b.N; i++ {
56+
u.RawQuery = rq
57+
router.ServeHTTP(w, r)
58+
}
59+
}
60+
61+
func benchRoutes(b *testing.B, router http.Handler, routes []route) {
62+
w := new(mockResponseWriter)
63+
r, _ := http.NewRequest("GET", "/", nil)
64+
u := r.URL
65+
rq := u.RawQuery
66+
67+
b.ReportAllocs()
68+
b.ResetTimer()
69+
70+
for i := 0; i < b.N; i++ {
71+
for _, route := range routes {
72+
r.Method = route.method
73+
u.Path = route.path
74+
u.RawQuery = rq
75+
router.ServeHTTP(w, r)
76+
}
77+
}
78+
}
79+
80+
// Common
81+
func httpHandlerFunc(w http.ResponseWriter, r *http.Request) {}
82+
83+
// gocraft/web
84+
type gocraftWebContext struct{}
85+
86+
func gocraftWebHandler(w web.ResponseWriter, r *web.Request) {}
87+
88+
func gocraftWebHandlerWrite(w web.ResponseWriter, r *web.Request) {
89+
io.WriteString(w, r.PathParams["name"])
90+
}
91+
92+
func loadGocraftWeb(routes []route) *web.Router {
93+
router := web.New(gocraftWebContext{})
94+
for _, route := range routes {
95+
switch route.method {
96+
case "GET":
97+
router.Get(route.path, gocraftWebHandler)
98+
case "POST":
99+
router.Post(route.path, gocraftWebHandler)
100+
case "PUT":
101+
router.Put(route.path, gocraftWebHandler)
102+
case "PATCH":
103+
router.Patch(route.path, gocraftWebHandler)
104+
case "DELETE":
105+
router.Delete(route.path, gocraftWebHandler)
106+
default:
107+
panic("Unknow HTTP method: " + route.method)
108+
}
109+
}
110+
return router
111+
}
112+
113+
// gorilla/mux
114+
func gorillaHandlerWrite(w http.ResponseWriter, r *http.Request) {
115+
params := mux.Vars(r)
116+
io.WriteString(w, params["name"])
117+
}
118+
119+
func loadGorillaMux(routes []route) *mux.Router {
120+
re := regexp.MustCompile(":([^/]*)")
121+
m := mux.NewRouter()
122+
for _, route := range routes {
123+
m.HandleFunc(re.ReplaceAllString(route.path, "{$1}"), httpHandlerFunc).Methods(route.method)
124+
}
125+
return m
126+
}
127+
128+
// HttpRouter
129+
func httpRouterHandle(w http.ResponseWriter, r *http.Request, _ map[string]string) {}
130+
131+
func httpRouterHandleWrite(w http.ResponseWriter, r *http.Request, vars map[string]string) {
132+
io.WriteString(w, vars["name"])
133+
}
134+
135+
func loadHttpRouter(routes []route) *httprouter.Router {
136+
router := httprouter.New()
137+
for _, route := range routes {
138+
router.Handle(route.method, route.path, httpRouterHandle)
139+
}
140+
return router
141+
}
142+
143+
// Martini
144+
func martiniHandler() {}
145+
146+
func martiniHandlerWrite(params martini.Params) string {
147+
return params["name"]
148+
}
149+
150+
func loadMartini(routes []route) *martini.Martini {
151+
router := martini.NewRouter()
152+
for _, route := range routes {
153+
switch route.method {
154+
case "GET":
155+
router.Get(route.path, martiniHandler)
156+
case "POST":
157+
router.Post(route.path, martiniHandler)
158+
case "PUT":
159+
router.Put(route.path, martiniHandler)
160+
case "PATCH":
161+
router.Patch(route.path, martiniHandler)
162+
case "DELETE":
163+
router.Delete(route.path, martiniHandler)
164+
default:
165+
panic("Unknow HTTP method: " + route.method)
166+
}
167+
}
168+
martini := martini.New()
169+
martini.Action(router.Handle)
170+
return martini
171+
}
172+
173+
// pat
174+
func patHandlerWrite(w http.ResponseWriter, r *http.Request) {
175+
io.WriteString(w, r.URL.Query().Get(":name"))
176+
}
177+
178+
func loadPat(routes []route) *pat.PatternServeMux {
179+
m := pat.New()
180+
for _, route := range routes {
181+
switch route.method {
182+
case "GET":
183+
m.Get(route.path, http.HandlerFunc(httpHandlerFunc))
184+
case "POST":
185+
m.Post(route.path, http.HandlerFunc(httpHandlerFunc))
186+
case "PUT":
187+
m.Put(route.path, http.HandlerFunc(httpHandlerFunc))
188+
case "DELETE":
189+
m.Del(route.path, http.HandlerFunc(httpHandlerFunc))
190+
default:
191+
panic("Unknow HTTP method: " + route.method)
192+
}
193+
}
194+
return m
195+
}
196+
197+
// Tiger Tonic
198+
func tigerTonicHandlerWrite(w http.ResponseWriter, r *http.Request) {
199+
io.WriteString(w, r.URL.Query().Get("name"))
200+
}
201+
202+
func loadTigerTonic(routes []route) *tigertonic.TrieServeMux {
203+
re := regexp.MustCompile(":([^/]*)")
204+
mux := tigertonic.NewTrieServeMux()
205+
for _, route := range routes {
206+
mux.HandleFunc(route.method, re.ReplaceAllString(route.path, "{$1}"), httpHandlerFunc)
207+
}
208+
return mux
209+
}
210+
211+
// Traffic
212+
func trafficHandlerWrite(w traffic.ResponseWriter, r *traffic.Request) {
213+
io.WriteString(w, r.URL.Query().Get("name"))
214+
}
215+
func trafficHandler(w traffic.ResponseWriter, r *traffic.Request) {}
216+
217+
func loadTraffic(routes []route) *traffic.Router {
218+
traffic.SetVar("env", "bench")
219+
router := traffic.New()
220+
for _, route := range routes {
221+
switch route.method {
222+
case "GET":
223+
router.Get(route.path, trafficHandler)
224+
case "POST":
225+
router.Post(route.path, trafficHandler)
226+
case "PUT":
227+
router.Put(route.path, trafficHandler)
228+
case "PATCH":
229+
router.Patch(route.path, trafficHandler)
230+
case "DELETE":
231+
router.Delete(route.path, trafficHandler)
232+
default:
233+
panic("Unknow HTTP method: " + route.method)
234+
}
235+
}
236+
return router
237+
}
238+
239+
// Micro Benchmarks
240+
241+
// Route with Param (no write)
242+
func BenchmarkGocraftWeb_Param(b *testing.B) {
243+
router := web.New(gocraftWebContext{})
244+
router.Get("/user/:name", gocraftWebHandler)
245+
246+
r, _ := http.NewRequest("GET", "/user/gordon", nil)
247+
benchRequest(b, router, r)
248+
}
249+
func BenchmarkGorilla_Param(b *testing.B) {
250+
router := mux.NewRouter()
251+
router.HandleFunc("/user/{name}", httpHandlerFunc).Methods("GET")
252+
253+
r, _ := http.NewRequest("GET", "/user/gordon", nil)
254+
benchRequest(b, router, r)
255+
}
256+
func BenchmarkHttpRouter_Param(b *testing.B) {
257+
router := httprouter.New()
258+
router.GET("/user/:name", httpRouterHandle)
259+
260+
r, _ := http.NewRequest("GET", "/user/gordon", nil)
261+
benchRequest(b, router, r)
262+
}
263+
func BenchmarkMartini_Param(b *testing.B) {
264+
router := martini.NewRouter()
265+
router.Get("/user/:name", martiniHandler)
266+
martini := martini.New()
267+
martini.Action(router.Handle)
268+
269+
r, _ := http.NewRequest("GET", "/user/gordon", nil)
270+
benchRequest(b, martini, r)
271+
}
272+
func BenchmarkPat_Param(b *testing.B) {
273+
router := pat.New()
274+
router.Get("/user/:name", http.HandlerFunc(httpHandlerFunc))
275+
276+
w := new(mockResponseWriter)
277+
b.ReportAllocs()
278+
b.ResetTimer()
279+
280+
for i := 0; i < b.N; i++ {
281+
r, _ := http.NewRequest("GET", "/user/gordon", nil)
282+
router.ServeHTTP(w, r)
283+
}
284+
285+
//benchRequest(b, router, r)
286+
}
287+
func BenchmarkTigerTonic_Param(b *testing.B) {
288+
router := tigertonic.NewTrieServeMux()
289+
router.HandleFunc("GET", "/user/{name}", httpHandlerFunc)
290+
291+
r, _ := http.NewRequest("GET", "/user/gordon", nil)
292+
benchRequest(b, router, r)
293+
}
294+
func BenchmarkTraffic_Param(b *testing.B) {
295+
traffic.SetVar("env", "bench")
296+
router := traffic.New()
297+
router.Get("/user/:name", trafficHandler)
298+
299+
r, _ := http.NewRequest("GET", "/user/gordon", nil)
300+
benchRequest(b, router, r)
301+
}
302+
303+
// Route with Param and write
304+
func BenchmarkGocraftWeb_ParamWrite(b *testing.B) {
305+
router := web.New(gocraftWebContext{})
306+
router.Get("/user/:name", gocraftWebHandlerWrite)
307+
308+
r, _ := http.NewRequest("GET", "/user/gordon", nil)
309+
benchRequest(b, router, r)
310+
}
311+
func BenchmarkGorilla_ParamWrite(b *testing.B) {
312+
router := mux.NewRouter()
313+
router.HandleFunc("/user/{name}", gorillaHandlerWrite).Methods("GET")
314+
315+
r, _ := http.NewRequest("GET", "/user/gordon", nil)
316+
benchRequest(b, router, r)
317+
}
318+
func BenchmarkHttpRouter_ParamWrite(b *testing.B) {
319+
router := httprouter.New()
320+
router.GET("/user/:name", httpRouterHandleWrite)
321+
322+
r, _ := http.NewRequest("GET", "/user/gordon", nil)
323+
benchRequest(b, router, r)
324+
}
325+
func BenchmarkMartini_ParamWrite(b *testing.B) {
326+
router := martini.NewRouter()
327+
router.Get("/user/:name", martiniHandlerWrite)
328+
martini := martini.New()
329+
martini.Action(router.Handle)
330+
331+
r, _ := http.NewRequest("GET", "/user/gordon", nil)
332+
benchRequest(b, martini, r)
333+
}
334+
func BenchmarkPat_ParamWrite(b *testing.B) {
335+
router := pat.New()
336+
router.Get("/user/:name", http.HandlerFunc(patHandlerWrite))
337+
338+
r, _ := http.NewRequest("GET", "/user/gordon", nil)
339+
benchRequest(b, router, r)
340+
}
341+
func BenchmarkTigerTonic_ParamWrite(b *testing.B) {
342+
router := tigertonic.NewTrieServeMux()
343+
router.Handle("GET", "/user/{name}", http.HandlerFunc(tigerTonicHandlerWrite))
344+
r, _ := http.NewRequest("GET", "/user/gordon", nil)
345+
benchRequest(b, router, r)
346+
}
347+
func BenchmarkTraffic_ParamWrite(b *testing.B) {
348+
traffic.SetVar("env", "bench")
349+
router := traffic.New()
350+
router.Get("/user/:name", trafficHandlerWrite)
351+
352+
r, _ := http.NewRequest("GET", "/user/gordon", nil)
353+
benchRequest(b, router, r)
354+
}

0 commit comments

Comments
 (0)