Skip to content

Instantly share code, notes, and snippets.

@axdg
Created September 24, 2015 17:31
Show Gist options
  • Save axdg/44974b4c8aedd9fe583a to your computer and use it in GitHub Desktop.
Save axdg/44974b4c8aedd9fe583a to your computer and use it in GitHub Desktop.
github.com/axdg/echo
BenchmarkEcho_Param 20000000 114 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_Param5 10000000 217 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_Param20 2000000 634 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_ParamWrite 5000000 244 ns/op 16 B/op 1 allocs/op
BenchmarkEcho_GithubStatic 10000000 151 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_GithubParam 5000000 258 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_GithubAll 30000 54910 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_GPlusStatic 20000000 107 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_GPlusParam 10000000 148 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_GPlus2Params 10000000 218 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_GPlusAll 500000 2739 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_ParseStatic 20000000 110 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_ParseParam 10000000 130 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_Parse2Params 10000000 159 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_ParseAll 300000 4904 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_StaticAll 50000 38429 ns/op 0 B/op 0 allocs/op
github.com/labstack/echo
BenchmarkEcho_Param 20000000 107 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_Param5 10000000 209 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_Param20 2000000 613 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_ParamWrite 5000000 263 ns/op 16 B/op 1 allocs/op
BenchmarkEcho_GithubStatic 10000000 139 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_GithubParam 5000000 254 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_GithubAll 30000 55460 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_GPlusStatic 20000000 100 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_GPlusParam 10000000 143 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_GPlus2Params 10000000 206 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_GPlusAll 500000 2723 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_ParseStatic 20000000 106 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_ParseParam 10000000 127 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_Parse2Params 10000000 161 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_ParseAll 300000 5874 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_StaticAll 30000 43592 ns/op 0 B/op 0 allocs/op
package findmethod
// Mock Echo router for testing
type Router struct {
connectTree *node
deleteTree *node
getTree *node
headTree *node
optionsTree *node
patchTree *node
postTree *node
putTree *node
traceTree *node
}
// Mock Echo node for testing
type node struct {
label byte
}
// HTTP Method constants
const (
// CONNECT HTTP method
CONNECT = "CONNECT"
// DELETE HTTP method
DELETE = "DELETE"
// GET HTTP method
GET = "GET"
// HEAD HTTP method
HEAD = "HEAD"
// OPTIONS HTTP method
OPTIONS = "OPTIONS"
// PATCH HTTP method
PATCH = "PATCH"
// POST HTTP method
POST = "POST"
// PUT HTTP method
PUT = "PUT"
// TRACE HTTP method
TRACE = "TRACE"
)
// New router returns a new router
func NewRouter() *Router {
return &Router{
connectTree: new(node),
deleteTree: new(node),
getTree: new(node),
headTree: new(node),
optionsTree: new(node),
patchTree: new(node),
postTree: new(node),
putTree: new(node),
traceTree: new(node),
}
}
// findTreeWithSwitch
func (r *Router) findTreeWithSwitch(method string) (n *node) {
switch method {
case GET:
return r.getTree
case DELETE:
return r.deleteTree
case POST:
return r.postTree
case PUT:
return r.putTree
case OPTIONS:
return r.optionsTree
case PATCH:
return r.patchTree
case HEAD:
return r.headTree
case CONNECT:
return r.connectTree
case TRACE:
return r.traceTree
}
return nil
}
// findTreeWithSwitchOpt
func (r *Router) findTreeWithSwitchOpt(method string) (n *node) {
if method == GET {
return r.getTree
} else if method == POST {
return r.postTree
} else if method == DELETE {
return r.deleteTree
} else if method == PUT {
return r.putTree
}
switch method {
case OPTIONS:
return r.optionsTree
case PATCH:
return r.patchTree
case HEAD:
return r.headTree
case CONNECT:
return r.connectTree
case TRACE:
return r.traceTree
}
return nil
}
func findUncommonTree(r *Router, method string) (n *node) {
switch method {
case OPTIONS:
return r.optionsTree
case PATCH:
return r.patchTree
case HEAD:
return r.headTree
case CONNECT:
return r.connectTree
case TRACE:
return r.traceTree
}
return nil
}
func findCommonTree(r *Router, method string) (n *node) {
if method == POST {
return r.postTree
} else if method == DELETE {
return r.deleteTree
}
if method[1] == 0x55 {
if method[0] == 0x50 && method[2] == 0x54 {
return r.putTree
}
}
return findUncommonTree(r, method)
}
func (r *Router) findTreeWithSwitchOverOpt(method string) (n *node) {
if method == GET {
return r.getTree
}
return findCommonTree(r, method)
}
// findTreeWithHashSwitch
func (r *Router) findTreeWithHashSwitch(method string) (n *node) {
switch method[0] {
case 'G': // GET
m := uint32(method[2])<<8 | uint32(method[1])<<16 | uint32(method[0])<<24
if m == 0x47455400 {
n = r.getTree
}
case 'P': // POST, PUT or PATCH
switch method[1] {
case 'O': // POST
m := uint32(method[3]) | uint32(method[2])<<8 | uint32(method[1])<<16 |
uint32(method[0])<<24
if m == 0x504f5354 {
n = r.postTree
}
case 'U': // PUT
m := uint32(method[2])<<8 | uint32(method[1])<<16 | uint32(method[0])<<24
if m == 0x50555400 {
n = r.putTree
}
case 'A': // PATCH
m := uint64(method[4])<<24 | uint64(method[3])<<32 | uint64(method[2])<<40 |
uint64(method[1])<<48 | uint64(method[0])<<56
if m == 0x5041544348000000 {
n = r.patchTree
}
}
case 'D': // DELETE
m := uint64(method[5])<<16 | uint64(method[4])<<24 | uint64(method[3])<<32 |
uint64(method[2])<<40 | uint64(method[1])<<48 | uint64(method[0])<<56
if m == 0x44454c4554450000 {
n = r.deleteTree
}
case 'C': // CONNECT
m := uint64(method[6])<<8 | uint64(method[5])<<16 | uint64(method[4])<<24 |
uint64(method[3])<<32 | uint64(method[2])<<40 | uint64(method[1])<<48 |
uint64(method[0])<<56
if m == 0x434f4e4e45435400 {
n = r.connectTree
}
case 'H': // HEAD
m := uint32(method[3]) | uint32(method[2])<<8 | uint32(method[1])<<16 |
uint32(method[0])<<24
if m == 0x48454144 {
n = r.headTree
}
case 'O': // OPTIONS
m := uint64(method[6])<<8 | uint64(method[5])<<16 | uint64(method[4])<<24 |
uint64(method[3])<<32 | uint64(method[2])<<40 | uint64(method[1])<<48 |
uint64(method[0])<<56
if m == 0x4f5054494f4e5300 {
n = r.optionsTree
}
case 'T': // TRACE
m := uint64(method[4])<<24 | uint64(method[3])<<32 | uint64(method[2])<<40 |
uint64(method[1])<<48 | uint64(method[0])<<56
if m == 0x5452414345000000 {
n = r.traceTree
}
}
return
}
package findmethod
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestFindTreeWithSwitch(t *testing.T) {
r := NewRouter()
methodShort := "G"
methodLong := "GETTER"
m := r.findTreeWithSwitch(methodLong)
assert.Nil(t, m)
r.findTreeWithSwitch(methodShort)
defer func() {
v := recover()
assert.Nil(t, v)
}()
}
// Calling findTreeWithHashSwitch and passing in a string that matches
// the first letter of a valid HTTP method but is shorter than that method
// will cause an index out of range panic. Calling findTreeWithHashSwitch
// and passing in a string that begins with a valid HTTP method will cause
// that method to be matched.
func TestFindTreeWithHashSwitch(t *testing.T) {
r := NewRouter()
methodLong := "GETTER"
methodShort := "G"
m := r.findTreeWithHashSwitch(methodLong)
assert.Nil(t, m)
r.findTreeWithHashSwitch(methodShort)
defer func() {
v := recover()
assert.Nil(t, v)
}()
}
func BenchmarkFindTreeWithSwitch_GET(b *testing.B) {
r := NewRouter()
for n := 0; n < b.N; n++ {
r.findTreeWithSwitch(GET)
}
}
func BenchmarkFindTreeWithSwitchOpt_GET(b *testing.B) {
r := NewRouter()
for n := 0; n < b.N; n++ {
r.findTreeWithSwitchOpt(GET)
}
}
func BenchmarkFindTreeWithSwitchOverOpt_GET(b *testing.B) {
r := NewRouter()
for n := 0; n < b.N; n++ {
r.findTreeWithSwitchOverOpt(GET)
}
}
func BenchmarkFindTreeWithHashSwitch_GET(b *testing.B) {
r := NewRouter()
for n := 0; n < b.N; n++ {
r.findTreeWithHashSwitch(GET)
}
}
func BenchmarkFindTreeWithSwitch_DELETE(b *testing.B) {
r := NewRouter()
for n := 0; n < b.N; n++ {
r.findTreeWithSwitch(DELETE)
}
}
func BenchmarkFindTreeWithSwitchOpt_DELETE(b *testing.B) {
r := NewRouter()
for n := 0; n < b.N; n++ {
r.findTreeWithSwitchOpt(DELETE)
}
}
func BenchmarkFindTreeWithSwitchOverOpt_DELETE(b *testing.B) {
r := NewRouter()
for n := 0; n < b.N; n++ {
r.findTreeWithSwitchOverOpt(DELETE)
}
}
func BenchmarkFindTreeWithHashSwitch_DELETE(b *testing.B) {
r := NewRouter()
for n := 0; n < b.N; n++ {
r.findTreeWithHashSwitch(DELETE)
}
}
func BenchmarkFindTreeWithSwitch_POST(b *testing.B) {
r := NewRouter()
for n := 0; n < b.N; n++ {
r.findTreeWithSwitch(POST)
}
}
func BenchmarkFindTreeWithSwitchOpt_POST(b *testing.B) {
r := NewRouter()
for n := 0; n < b.N; n++ {
r.findTreeWithSwitchOpt(POST)
}
}
func BenchmarkFindTreeWithSwitchOverOpt_POST(b *testing.B) {
r := NewRouter()
for n := 0; n < b.N; n++ {
r.findTreeWithSwitchOverOpt(POST)
}
}
func BenchmarkFindTreeWithHashSwitch_POST(b *testing.B) {
r := NewRouter()
for n := 0; n < b.N; n++ {
r.findTreeWithHashSwitch(POST)
}
}
func BenchmarkFindTreeWithSwitch_PUT(b *testing.B) {
r := NewRouter()
for n := 0; n < b.N; n++ {
r.findTreeWithSwitch(PUT)
}
}
func BenchmarkFindTreeWithSwitchOpt_PUT(b *testing.B) {
r := NewRouter()
for n := 0; n < b.N; n++ {
r.findTreeWithSwitchOpt(PUT)
}
}
func BenchmarkFindTreeWithSwitchOverOpt_PUT(b *testing.B) {
r := NewRouter()
for n := 0; n < b.N; n++ {
r.findTreeWithSwitchOverOpt(PUT)
}
}
func BenchmarkFindTreeWithHashSwitch_PUT(b *testing.B) {
r := NewRouter()
for n := 0; n < b.N; n++ {
r.findTreeWithHashSwitch(PUT)
}
}
@axdg
Copy link
Author

axdg commented Sep 24, 2015

To run the tests:

$ go test

To run the benchmarks:

$ go test -run=None -bench=.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment