-
Notifications
You must be signed in to change notification settings - Fork 53
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 ReadString() and WriteString() #574
Conversation
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.
This looks good to me.
@r-hang PTAL.
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.
Logic looks good, I left a question about the structure of the benchmark.
Since this change concerns a go1.20 feature, I think we should merge this after #575 goes to dev.
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.
lgtm! I think you'll need to rebase to pull in the contents of #575.
@r-hang i think you pulled in the changes incorrectly to this branch. |
@sywhang, the author reached out after their rebase. I'm working with them now. |
@r-hang mb, apologies for assuming :p saw your commits show up all of a sudden so I assumed you pushed to the branch. Thanks. |
2591bc8
to
8f1f31b
Compare
After rebasing #574 on top of #577 and running GO111MODULE=on make lint I still see some errors. cmd/thriftbreak/main_test.go:26:2: "io/ioutil" has been deprecated since Go 1.19: As of Go 1.16, the same functionality is now provided by package io or package os, and those implementations should be preferred in new code. See the specific function documentation for details. (SA1019) gen/string.go:66:15: strings.Title has been deprecated since Go 1.18 and an alternative has been available since Go 1.0: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead. (SA1019) After this change: $ git log --oneline a054ce6 (HEAD -> optimise_read_string) Fix remaining lint errors 2c1d285 build tags 8a1aa7d address review comments f26170a inline 41dacbd remove bench reports 4ad4d9c optimise write string fb22d6e optimise readstring() 6268dfe (origin/dev, origin/HEAD) Remove all usages of io/ioutil (#577) $ GO111MODULE=on make lint Checking gofmt Checking govet Checking golint Checking staticcheck $ echo $? 0
After rebasing #574 on top of #577 and running GO111MODULE=on make lint I still see some errors. cmd/thriftbreak/main_test.go:26:2: "io/ioutil" has been deprecated since Go 1.19: As of Go 1.16, the same functionality is now provided by package io or package os, and those implementations should be preferred in new code. See the specific function documentation for details. (SA1019) gen/string.go:66:15: strings.Title has been deprecated since Go 1.18 and an alternative has been available since Go 1.0: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead. (SA1019) After this change: $ git log --oneline a054ce6 (HEAD -> optimise_read_string) Fix remaining lint errors 2c1d285 build tags 8a1aa7d address review comments f26170a inline 41dacbd remove bench reports 4ad4d9c optimise write string fb22d6e optimise readstring() 6268dfe (origin/dev, origin/HEAD) Remove all usages of io/ioutil (#577) $ GO111MODULE=on make lint Checking gofmt Checking govet Checking golint Checking staticcheck $ echo $? 0
After rebasing #574 on top of #577 and running GO111MODULE=on make lint I still see some errors. cmd/thriftbreak/main_test.go:26:2: "io/ioutil" has been deprecated since Go 1.19: As of Go 1.16, the same functionality is now provided by package io or package os, and those implementations should be preferred in new code. See the specific function documentation for details. (SA1019) gen/string.go:66:15: strings.Title has been deprecated since Go 1.18 and an alternative has been available since Go 1.0: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead. (SA1019) After this change: $ git log --oneline a054ce6 (HEAD -> optimise_read_string) Fix remaining lint errors 2c1d285 build tags 8a1aa7d address review comments f26170a inline 41dacbd remove bench reports 4ad4d9c optimise write string fb22d6e optimise readstring() 6268dfe (origin/dev, origin/HEAD) Remove all usages of io/ioutil (#577) $ GO111MODULE=on make lint Checking gofmt Checking govet Checking golint Checking staticcheck $ echo $? 0 I've cherry-picked the contents of the HEAD commit displayed above into a separate branch for this PR to dev.
@saurabhagrawal-86 we've updated dev again to resolve some outstanding staticcheck issues. Could you rebase again and see if the remote go1.19 and go1.20 builds pass? Your change built for me locally when I last tested it out once rebased on dev. |
4d27df8
to
1a079b6
Compare
// ReadString reads a Thrift encoded string. | ||
func (sr *StreamReader) ReadString() (string, error) { | ||
bs, err := sr.ReadBinary() | ||
return unsafe.String(unsafe.SliceData(bs), len(bs)), err |
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 the name implies, any use of unsafe
should be used very carefully. I'd strongly recommend adding a comment that indicates what assumptions are being made and why this is safe.
In this case, bs
returned from ReadBinary
has no mutable references (it creates a copy in that method), so it seems safe.
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.
An alternate implementation which avoids unsafe by using strings.Builder
(which internally uses unsafe) would also work, and avoiding separate implementations by version
var b strings.Builder
io.Copy(&b, sr.reader, length)
return b.String()
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.
Thanks for the suggestion. I've added a comment explaining why "unsafe" is safe to use here.
In your alternate implementation, I'm assuming you meant io.CopyN (not io.Copy). If yes, that ends up being even slower than the original implementation.
When copying from a bytes.Buffer to a strings.Builder, I understand that io.Copy would avoid an allocation and a copy. However, io.CopyN is unable to make those optimisations and allocates a staging area for the copy operation. That ends up outweighing the efficiency of the strings.Builder's String() function.
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.
benchmark results are here - saurabhagrawal-86#1
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.
@prashantv, the benchmark results and evaluation of io.CopyN looks fine to me. We'll wait for your follow up!
Codecov Report
@@ Coverage Diff @@
## dev #574 +/- ##
==========================================
+ Coverage 68.00% 68.01% +0.01%
==========================================
Files 140 142 +2
Lines 23872 23878 +6
==========================================
+ Hits 16233 16241 +8
+ Misses 4578 4577 -1
+ Partials 3061 3060 -1
📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
Optimise StreamReader's ReadString CPU by 35% and memory by 50% using the new
String
andSliceData
functions introduced in the unsafe package in go 1.20Similar changes to optimise StreamWriter's WriteString() method.