Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimise sanitize with string builder #2918

Closed
wants to merge 1 commit into from

Conversation

trkohler
Copy link
Member

@trkohler trkohler commented Feb 9, 2024

not a big difference, but on large query strings it's noticeable.
can be minor and safe enhancement?

go test -benchmem -run=^$ -bench ^BenchmarkStripQuery$ -benchtime=10s github.com/zalando/skipper/filters/builtin

BenchmarkStripQuery/[old_sanitize]_url_1-8              85732707               118.6 ns/op            80 B/op          4 allocs/op
BenchmarkStripQuery/[old_sanitize]_url_2-8              14885370               808.2 ns/op           472 B/op         15 allocs/op
BenchmarkStripQuery/[old_sanitize]_url_3-8              73297474               162.9 ns/op           120 B/op          4 allocs/op
BenchmarkStripQuery/[old_sanitize]_url_4-8              100000000              114.1 ns/op            80 B/op          4 allocs/op


BenchmarkStripQuery/[new_sanitize]_url_1-8              100000000              103.5 ns/op            24 B/op          3 allocs/op
BenchmarkStripQuery/[new_sanitize]_url_2-8              15854868               778.5 ns/op           192 B/op         14 allocs/op
BenchmarkStripQuery/[new_sanitize]_url_3-8              71689101               166.3 ns/op            64 B/op          4 allocs/op
BenchmarkStripQuery/[new_sanitize]_url_4-8              100000000              102.0 ns/op            24 B/op          3 allocs/op

@trkohler trkohler added the minor no risk changes, for example new filters label Feb 9, 2024
@@ -51,7 +51,8 @@ func validHeaderFieldByte(b byte) bool {
}

// make sure we don't generate invalid headers
func sanitize(input string) string {
// temporary public function to benchmark it
Copy link
Member

@AlexanderYastrebov AlexanderYastrebov Feb 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need to export it as benchmarks sits in the same package.
Usually we introduce benchmark as a first commit and include baseline benchmark results in the commit message.
Then change implementation, re-run benchmark and compare results to the baseline via https://pkg.go.dev/golang.org/x/perf/cmd/benchstat

To get a statistically meaningful benchmark results it should be run several times, usually we run 10.
See this PR as an example of performance-related change #2870

I've also created a small script to automate benchmarking two commits (its important that benchmark function is added in a separate commit to establish the baseline), see golang/go#63233 (comment)

Comment on lines +68 to +75
toAscii := strconv.QuoteToASCII(input)
var s strings.Builder
for _, i := range toAscii {
if validHeaderFieldByte(byte(i)) {
s.WriteRune(i)
}
}
return s.String()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To get ultimate performance we can use byte version https://pkg.go.dev/strconv#AppendQuoteToASCII here and then replace invalid bytes with - instead of allocating another slice.

}

for i, v := range table {
url, _ := url.ParseRequestURI(v.url)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is redundant, test cases should contain query string instead of url

Comment on lines +119 to +129
for i, v := range table {
url, _ := url.ParseRequestURI(v.url)
q := url.Query()
b.Run(fmt.Sprintf("[new sanitize] url %d", i + 1), func(b *testing.B) {
for i := 0; i < b.N; i++ {
for k := range q {
NewSanitize(k)
}
}
})
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need to have a second benchmark, see comment above.

@@ -0,0 +1,14 @@
`go test -benchmem -run=^$ -bench ^BenchmarkStripQuery$ -benchtime=10s github.com/zalando/skipper/filters/builtin`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add results to the commit message as described in the comment above.

for i, v := range table {
url, _ := url.ParseRequestURI(v.url)
q := url.Query()
b.Run(fmt.Sprintf("[old sanitize] url %d", i+1), func(b *testing.B) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to add b.ReportAllocs() instead of -benchmem flag

@trkohler trkohler closed this Feb 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
minor no risk changes, for example new filters
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants