2
0
mirror of https://github.com/soheilhy/cmux.git synced 2024-09-20 02:55:46 +08:00

Optimize Patricia tree

Remove all the extra allocations in the Patricia tree.

O(1) allocation for Patricia and  ~10% improvement for HTTP1 matching.

benchmark                      old ns/op     new ns/op     delta
BenchmarkCMuxConnHTTP1-4       908           782           -13.88%
BenchmarkCMuxConnHTTP2-4       835           818           -2.04%
BenchmarkCMuxConnHTTP1n2-4     1074          1033          -3.82%
BenchmarkCMuxConnHTTP2n1-4     1010          901           -10.79%

benchmark                      old allocs     new allocs     delta
BenchmarkCMuxConnHTTP1-4       5              3              -40.00%
BenchmarkCMuxConnHTTP2-4       4              4              +0.00%
BenchmarkCMuxConnHTTP1n2-4     6              4              -33.33%
BenchmarkCMuxConnHTTP2n1-4     6              4              -33.33%

benchmark                      old bytes     new bytes     delta
BenchmarkCMuxConnHTTP1-4       276           272           -1.45%
BenchmarkCMuxConnHTTP2-4       304           304           +0.00%
BenchmarkCMuxConnHTTP1n2-4     306           304           -0.65%
BenchmarkCMuxConnHTTP2n1-4     308           304           -1.30%
This commit is contained in:
Soheil Hassas Yeganeh 2016-05-03 21:52:24 -04:00
parent d45bcbe1db
commit 703b087a39

View File

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