ioptimized decodeArgAppend a bit

Benchmark results on linux/amd64:

name                           old time/op  new time/op  delta
ArgsParse-4                    72.8ns ± 2%  68.0ns ± 2%  -6.59%  (p=0.000 n=10+9)
AppendUnquotedArgFastPath-4    20.4ns ± 2%  21.1ns ± 9%    ~     (p=0.614 n=8+10)
AppendUnquotedArgSlowPath-4    68.9ns ± 3%  70.4ns ± 6%    ~     (p=0.148 n=9+10)
URIParsePath-4                 80.9ns ± 2%  78.7ns ± 2%  -2.80%  (p=0.000 n=10+10)
URIParsePathQueryString-4      88.9ns ± 1%  86.3ns ± 1%  -2.90%  (p=0.000 n=10+8)
URIParsePathQueryStringHash-4  95.7ns ± 8%  91.0ns ± 1%  -4.88%  (p=0.000 n=9+10)
URIParseHostname-4             98.6ns ± 1%  95.4ns ± 1%  -3.24%  (p=0.000 n=10+10)
This commit is contained in:
Aliaksandr Valialkin
2017-07-21 16:08:18 +03:00
parent 52a0993b96
commit d257ae60a3
4 changed files with 53 additions and 36 deletions
+45 -12
View File
@@ -428,15 +428,15 @@ func (s *argsScanner) next(kv *argsKV) bool {
case '=':
if isKey {
isKey = false
kv.key = decodeArgAppend(kv.key[:0], s.b[:i], true)
kv.key = decodeArgAppend(kv.key[:0], s.b[:i])
k = i + 1
}
case '&':
if isKey {
kv.key = decodeArgAppend(kv.key[:0], s.b[:i], true)
kv.key = decodeArgAppend(kv.key[:0], s.b[:i])
kv.value = kv.value[:0]
} else {
kv.value = decodeArgAppend(kv.value[:0], s.b[k:i], true)
kv.value = decodeArgAppend(kv.value[:0], s.b[k:i])
}
s.b = s.b[i+1:]
return true
@@ -444,17 +444,17 @@ func (s *argsScanner) next(kv *argsKV) bool {
}
if isKey {
kv.key = decodeArgAppend(kv.key[:0], s.b, true)
kv.key = decodeArgAppend(kv.key[:0], s.b)
kv.value = kv.value[:0]
} else {
kv.value = decodeArgAppend(kv.value[:0], s.b[k:], true)
kv.value = decodeArgAppend(kv.value[:0], s.b[k:])
}
s.b = s.b[len(s.b):]
return true
}
func decodeArgAppend(dst, src []byte, decodePlus bool) []byte {
if bytes.IndexByte(src, '%') < 0 && (!decodePlus || bytes.IndexByte(src, '+') < 0) {
func decodeArgAppend(dst, src []byte) []byte {
if bytes.IndexByte(src, '%') < 0 && bytes.IndexByte(src, '+') < 0 {
// fast path: src doesn't contain encoded chars
return append(dst, src...)
}
@@ -466,15 +466,15 @@ func decodeArgAppend(dst, src []byte, decodePlus bool) []byte {
if i+2 >= n {
return append(dst, src[i:]...)
}
x1 := hexbyte2int(src[i+1])
x2 := hexbyte2int(src[i+2])
if x1 < 0 || x2 < 0 {
x1 := hex2intTable[src[i+1]]
x2 := hex2intTable[src[i+2]]
if x1 == 16 || x2 == 16 {
dst = append(dst, c)
} else {
dst = append(dst, byte(x1<<4|x2))
dst = append(dst, x1<<4|x2)
i += 2
}
} else if decodePlus && c == '+' {
} else if c == '+' {
dst = append(dst, ' ')
} else {
dst = append(dst, c)
@@ -482,3 +482,36 @@ func decodeArgAppend(dst, src []byte, decodePlus bool) []byte {
}
return dst
}
// decodeArgAppendNoPlus is almost identical to decodeArgAppend, but it doesn't
// substitute '+' with ' '.
//
// The function is copy-pasted from decodeArgAppend due to the preformance
// reasons only.
func decodeArgAppendNoPlus(dst, src []byte) []byte {
if bytes.IndexByte(src, '%') < 0 {
// fast path: src doesn't contain encoded chars
return append(dst, src...)
}
// slow path
for i, n := 0, len(src); i < n; i++ {
c := src[i]
if c == '%' {
if i+2 >= n {
return append(dst, src[i:]...)
}
x1 := hex2intTable[src[i+1]]
x2 := hex2intTable[src[i+2]]
if x1 == 16 || x2 == 16 {
dst = append(dst, c)
} else {
dst = append(dst, x1<<4|x2)
i += 2
}
} else {
dst = append(dst, c)
}
}
return dst
}
+7 -11
View File
@@ -265,8 +265,8 @@ func readHexInt(r *bufio.Reader) (int, error) {
}
return -1, err
}
k = hexbyte2int(c)
if k < 0 {
k = int(hex2intTable[c])
if k == 16 {
if i == 0 {
return -1, errEmptyHexNum
}
@@ -324,23 +324,19 @@ func hexCharUpper(c byte) byte {
var hex2intTable = func() []byte {
b := make([]byte, 255)
for i := byte(0); i < 255; i++ {
c := byte(0)
c := byte(16)
if i >= '0' && i <= '9' {
c = 1 + i - '0'
c = i - '0'
} else if i >= 'a' && i <= 'f' {
c = 1 + i - 'a' + 10
c = i - 'a' + 10
} else if i >= 'A' && i <= 'F' {
c = 1 + i - 'A' + 10
c = i - 'A' + 10
}
b[i] = c
}
return b
}()
func hexbyte2int(c byte) int {
return int(hex2intTable[c]) - 1
}
const toLower = 'a' - 'A'
var toLowerTable = func() [256]byte {
@@ -401,7 +397,7 @@ func s2b(s string) []byte {
//
// dst may point to src. In this case src will be overwritten.
func AppendUnquotedArg(dst, src []byte) []byte {
return decodeArgAppend(dst, src, true)
return decodeArgAppend(dst, src)
}
// AppendQuotedArg appends url-encoded src to dst and returns appended dst.
-12
View File
@@ -75,18 +75,6 @@ func BenchmarkInt2HexByte(b *testing.B) {
})
}
func BenchmarkHexByte2Int(b *testing.B) {
buf := []byte("0A1B2c3d4E5F6C7a8D9ab7cd03ef")
b.RunParallel(func(pb *testing.PB) {
var c byte
for pb.Next() {
for _, c = range buf {
hexbyte2int(c)
}
}
})
}
func BenchmarkWriteHexInt(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
var w ByteBuffer
+1 -1
View File
@@ -277,7 +277,7 @@ func (u *URI) parse(host, uri []byte, h *RequestHeader) {
func normalizePath(dst, src []byte) []byte {
dst = dst[:0]
dst = addLeadingSlash(dst, src)
dst = decodeArgAppend(dst, src, false)
dst = decodeArgAppendNoPlus(dst, src)
// remove duplicate slashes
b := dst