-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
Go version
go1.22.2
golang/x/net v0.24.0
Output of go env in your module/workspace:
GOARCH='amd64'
GOOS='darwin'What did you do?
I noticed that httpguts.IsTokenRune, which httpguts.ValidHeaderFieldName calls iteratively and which net/http in general relies on, systematically incurs a bounds check. I then wrote an alternative implementation that eliminates those bounds checks and also obviates the need for UTF-8 decoding.
package p
import "unicode/utf8"
func ValidHeaderFieldName(v string) bool {
return isToken(v)
}
func isToken(s string) bool {
if len(s) == 0 {
return false
}
for i := 0; i < len(s); i++ {
if !validTokenByte[s[i]] {
return false
}
}
return true
}
func IsTokenRune(r rune) bool {
return r < utf8.SelfRune && validTokenByte[byte(r)]
}
var validTokenByte = [256]bool{
'!': true,
'#': true,
'$': true,
'%': true,
'&': true,
'\'': true,
'*': true,
'+': true,
'-': true,
'.': true,
'0': true,
'1': true,
// rest omitted for brevity
'~': true,
}What did you see happen?
Benchmarks indicate that my implementation is about twice as fast as the current one; see below.
Moreover, the same logic could be used to validate HTTP methods and cookie names in addition to HTTP header-field names, since all three share the same production (token). At the moment, their validation logic, because it relies on strings.IndexFunc and httpguts.IsTokenRune, isn't as fast as it could, as shown by one of my benchmarks.
goos: darwin
goarch: amd64
pkg: github.com/jub0bs/httpguts-perf-exp
cpu: Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz
│ std │ jub0bs │
│ sec/op │ sec/op vs base │
IsCookieNameValid-8 1615.0n ± 0% 363.8n ± 1% -77.48% (p=0.000 n=20)
ValidHeaderFieldName-8 669.3n ± 1% 328.1n ± 0% -50.99% (p=0.000 n=20)
IsTokenRune-8 594.8n ± 0% 592.2n ± 0% -0.45% (p=0.004 n=20)
geomean 863.1n 413.4n -52.10%
│ std │ jub0bs │
│ B/op │ B/op vs base │
IsCookieNameValid-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
ValidHeaderFieldName-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
IsTokenRune-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
geomean ² +0.00% ²
¹ all samples are equal
² summaries must be >0 to compute geomean
│ std │ jub0bs │
│ allocs/op │ allocs/op vs base │
IsCookieNameValid-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
ValidHeaderFieldName-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
IsTokenRune-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
geomean ² +0.00% ²
¹ all samples are equal
² summaries must be >0 to compute geomeanWhat did you expect to see?
I expected at least some speedup of httpguts.ValidHeaderFieldName.
I have not yet checked how such a change would affect the performance of net/http, though.