2
0
mirror of https://github.com/soheilhy/cmux.git synced 2024-11-09 19:21:52 +08:00

Merge pull request #25 from soheilhy/devel-optimize-patricia

Optimize Patricia tree
This commit is contained in:
Soheil Hassas Yeganeh 2016-05-03 22:33:52 -04:00
commit 9d1e2a64dd
2 changed files with 113 additions and 56 deletions

View File

@ -6,8 +6,20 @@ import (
"net"
"sync"
"testing"
"golang.org/x/net/http2"
)
var (
benchHTTP1Payload = make([]byte, 4096)
benchHTTP2Payload = make([]byte, 4096)
)
func init() {
copy(benchHTTP1Payload, []byte("GET http://www.w3.org/ HTTP/1.1"))
copy(benchHTTP2Payload, http2.ClientPreface)
}
type mockConn struct {
net.Conn
r io.Reader
@ -17,20 +29,19 @@ func (c *mockConn) Read(b []byte) (n int, err error) {
return c.r.Read(b)
}
func BenchmarkCMuxConn(b *testing.B) {
benchHTTPPayload := make([]byte, 4096)
copy(benchHTTPPayload, []byte("GET http://www.w3.org/ HTTP/1.1"))
func discard(l net.Listener) {
for {
if _, err := l.Accept(); err != nil {
return
}
}
}
func BenchmarkCMuxConnHTTP1(b *testing.B) {
m := New(nil).(*cMux)
l := m.Match(HTTP1Fast())
go func() {
for {
if _, err := l.Accept(); err != nil {
return
}
}
}()
go discard(l)
donec := make(chan struct{})
var wg sync.WaitGroup
@ -39,7 +50,67 @@ func BenchmarkCMuxConn(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
c := &mockConn{
r: bytes.NewReader(benchHTTPPayload),
r: bytes.NewReader(benchHTTP1Payload),
}
m.serve(c, donec, &wg)
}
}
func BenchmarkCMuxConnHTTP2(b *testing.B) {
m := New(nil).(*cMux)
l := m.Match(HTTP2())
go discard(l)
donec := make(chan struct{})
var wg sync.WaitGroup
wg.Add(b.N)
b.ResetTimer()
for i := 0; i < b.N; i++ {
c := &mockConn{
r: bytes.NewReader(benchHTTP2Payload),
}
m.serve(c, donec, &wg)
}
}
func BenchmarkCMuxConnHTTP1n2(b *testing.B) {
m := New(nil).(*cMux)
l1 := m.Match(HTTP1Fast())
l2 := m.Match(HTTP2())
go discard(l1)
go discard(l2)
donec := make(chan struct{})
var wg sync.WaitGroup
wg.Add(b.N)
b.ResetTimer()
for i := 0; i < b.N; i++ {
c := &mockConn{
r: bytes.NewReader(benchHTTP2Payload),
}
m.serve(c, donec, &wg)
}
}
func BenchmarkCMuxConnHTTP2n1(b *testing.B) {
m := New(nil).(*cMux)
l2 := m.Match(HTTP2())
l1 := m.Match(HTTP1Fast())
go discard(l1)
go discard(l2)
donec := make(chan struct{})
var wg sync.WaitGroup
wg.Add(b.N)
b.ResetTimer()
for i := 0; i < b.N; i++ {
c := &mockConn{
r: bytes.NewReader(benchHTTP1Payload),
}
m.serve(c, donec, &wg)
}

View File

@ -9,11 +9,19 @@ import (
// and cannot be changed after instantiation.
type patriciaTree struct {
root *ptNode
buf []byte // preallocated buffer to read data while matching
}
func newPatriciaTree(b ...[]byte) *patriciaTree {
func newPatriciaTree(bs ...[]byte) *patriciaTree {
max := 0
for _, b := range bs {
if max < len(b) {
max = len(b)
}
}
return &patriciaTree{
root: newNode(b),
root: newNode(bs),
buf: make([]byte, max+1),
}
}
@ -22,17 +30,17 @@ func newPatriciaTreeString(strs ...string) *patriciaTree {
for i, s := range strs {
b[i] = []byte(s)
}
return &patriciaTree{
root: newNode(b),
}
return newPatriciaTree(b...)
}
func (t *patriciaTree) matchPrefix(r io.Reader) bool {
return t.root.match(r, true)
n, _ := io.ReadFull(r, t.buf)
return t.root.match(t.buf[:n], true)
}
func (t *patriciaTree) match(r io.Reader) bool {
return t.root.match(r, false)
n, _ := io.ReadFull(r, t.buf)
return t.root.match(t.buf[:n], false)
}
type ptNode struct {
@ -122,52 +130,30 @@ func splitPrefix(bss [][]byte) (prefix []byte, rest [][]byte) {
return prefix, rest
}
func readBytes(r io.Reader, n int) (b []byte, err error) {
b = make([]byte, n)
o := 0
for o < n {
nr, err := r.Read(b[o:])
if err != nil && err != io.EOF {
return b, err
func (n *ptNode) match(b []byte, prefix bool) bool {
l := len(n.prefix)
if l > 0 {
if l > len(b) {
l = len(b)
}
o += nr
if err == io.EOF {
break
}
}
return b[:o], nil
}
func (n *ptNode) match(r io.Reader, prefix bool) bool {
if l := len(n.prefix); l > 0 {
b, err := readBytes(r, l)
if err != nil || len(b) != l || !bytes.Equal(b, n.prefix) {
if !bytes.Equal(b[:l], n.prefix) {
return false
}
}
if prefix && n.terminal {
if n.terminal && (prefix || len(n.prefix) == len(b)) {
return true
}
b := make([]byte, 1)
for {
nr, err := r.Read(b)
if nr != 0 {
break
}
if err == io.EOF {
return n.terminal
}
if err != nil {
return false
}
nextN, ok := n.next[b[l]]
if !ok {
return false
}
nextN, ok := n.next[b[0]]
return ok && nextN.match(r, prefix)
if l == len(b) {
b = b[l:l]
} else {
b = b[l+1:]
}
return nextN.match(b, prefix)
}