-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
go/vt/sqlparser: improve performance in TrackedBuffer formatting #16364
Conversation
Review ChecklistHello reviewers! 👋 Please follow this checklist when reviewing this Pull Request. General
Tests
Documentation
New flags
If a workflow is added or modified:
Backward compatibility
|
@@ -288,14 +314,26 @@ func areBothISExpr(op Expr, val Expr) bool { | |||
// WriteArg writes a value argument into the buffer along with | |||
// tracking information for future substitutions. | |||
func (buf *TrackedBuffer) WriteArg(prefix, arg string) { | |||
length := len(prefix) + len(arg) | |||
buf.bindLocations = append(buf.bindLocations, BindLocation{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a potential follow up:
I got here because WriteArg
stood out a bit in a pprof I was looking at for heap allocations, and the other thing that can be done in here with some more work is the bindLocations
slice that keeps getting appended to 1 at a time.
So we might want to do a thing where we count up the args first so can we grow the slice, then append to the slice. So adding a method like, GrowArgs(size)
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #16364 +/- ##
=======================================
Coverage 68.91% 68.92%
=======================================
Files 1566 1566
Lines 201855 201885 +30
=======================================
+ Hits 139113 139147 +34
+ Misses 62742 62738 -4 ☔ View full report in Codecov by Sentry. |
f4f6579
to
c1a0848
Compare
Two minor things: * decently improve formatting integers (%d) * WriteArg calls buf.Grow to avoid potentially 2 allocations For integer formatting, buf.WriteString(fmt.Sprintf(...)) was about the worst way to do it. fmt.Sprintf itself allocates a new string, plus it's %d formatting is much more robust in handling padding and whatnot. First alternative for free win was using `fmt.Fprintf(&buf, ...)` instead, which simply avoids the extra allocation and just writes directly to the buffer. But strconv.Format{I,Ui}nt is quite fast, especially since it has a fast path for "small integers". Here's a trivial benchmark of all 3 options: ``` $ benchstat fmt.txt goos: darwin goarch: arm64 pkg: x │ fmt.txt │ │ sec/op │ FormatInt/Sprintf-10 55.51n ± 1% FormatInt/Fprintf-10 50.76n ± 1% FormatInt/FormatInt-10 17.16n ± 2% geomean 36.43n │ fmt.txt │ │ B/op │ FormatInt/Sprintf-10 16.00 ± 0% FormatInt/Fprintf-10 8.000 ± 0% FormatInt/FormatInt-10 8.000 ± 0% geomean 10.08 │ fmt.txt │ │ allocs/op │ FormatInt/Sprintf-10 2.000 ± 0% FormatInt/Fprintf-10 1.000 ± 0% FormatInt/FormatInt-10 1.000 ± 0% geomean 1.260 ``` So each %d within a format string is considerably faster. This obviously scales linearly with the number of %d's there are. Signed-off-by: Matt Robenolt <[email protected]>
c1a0848
to
4cc46b0
Compare
@mattrobenolt would you mind creating an umbrella issue like "VTGate performance improvements" or something similar and referring to that in this series of PRs? |
This PR is being marked as stale because it has been open for 30 days with no activity. To rectify, you may do any of the following:
If no action is taken within 7 days, this PR will be closed. |
This PR is being marked as stale because it has been open for 30 days with no activity. To rectify, you may do any of the following:
If no action is taken within 7 days, this PR will be closed. |
|
||
// WriteUint writes an unsigned integer into the buffer. | ||
func (buf *TrackedBuffer) WriteUint(v uint64) { | ||
buf.WriteString(strconv.FormatUint(v, 10)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this is still allocating memory when the small-number optimization doesn't trigger, so that isn't great. I think it may be time to leave behind the *strings.Builder
used here and instead implementing building ourselves, which would allow us to simplify many things, including using strconv.AppendInt
instead of the Format
versions. Maybe for another PR!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I assume you're thinking something more specialized like what zap
does? https://github.com/uber-go/zap/blob/master/buffer/buffer.go
Signed-off-by: Manan Gupta <[email protected]>
Two minor things:
For integer formatting, buf.WriteString(fmt.Sprintf(...)) was about the worst way to do it. fmt.Sprintf itself allocates a new string, plus it's %d formatting is much more robust in handling padding and whatnot.
First alternative for free win was using
fmt.Fprintf(&buf, ...)
instead, which simply avoids the extra allocation and just writes directly to the buffer.But strconv.Format{I,Ui}nt is quite fast, especially since it has a fast path for "small integers".
Here's a trivial benchmark of all 3 options:
So each %d within a format string is considerably faster. This obviously scales linearly with the number of %d's there are.
Related Issue(s)
Part of #16789
Checklist