-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
proposal: io/v2: CopyBuffer should avoid ReadFrom/WriteTo #16474
Comments
Why would someone implement I see what you mean with the buf. It doesn't make much sense to pass in a buffer when I don't think we can change this because of the Go 1 compatibility anyway. |
ReaderFrom does not have to be horribly inefficient, but it can be inefficient enough for the CopyBuffer caller to care. In my case, this was severely reducing the file transfer rate but it might be totally fine for some other application. Someone probably added it for a different use case, without realizing the side effects on CopyBuffer. |
Well we can't really change this now. Perhaps in Go 2? |
@nhooyr, how do you see this as a Go 1 compatibility issue? Whether Part of the problem is that |
He said that Furthermore, a lot of code relies on the I definitely agree though, I find it unintuitive that |
|
Fair argument. |
@nhooyr, thanks for the clarification. Backwards compatibility argument makes sense, that's why I said "conceptually" ;) |
Just noting here that It relies on Related to #13848, which is intended to help alleviate the issue. |
@dsnet Is this an actual bottleneck in something that can't be fixed without stdlib changes? |
My original post contained the hack to get around this issue: io.CopyBuffer(struct{ io.Writer }{dst}, struct{ io.Reader }{src}, buf) It's just not an intuitive hack to most people. Secondly, the need for it doesn't become obvious until a pprof indicates that |
@dsnet Was it ever an observed bottleneck in a real, otherwise well-written, system? |
Yes, #13848 was filed when I observed a large string was unnecessarily being copied. |
Haha, I just got bitten by this. In tlsmuxd I was copying between two TCP connections with Luckily, I read |
Not performance related, but http://golang.org/cl/31174 is another case where the behavior of |
Here's a different way to look at this issue, which generalizes to magic-method APIs in general: For a magic-method API to work with wrapper types, the method's implementation must be able to indicate that the method is not actually supported.
You could imagine retrofitting one, such as |
This is one of the driving usages of #21904. Part of the problem with "not implemented" is that it's obvious for cases like |
@bcmills I don't think this works in general. In the case discussed here, it sometimes makes sense for More generally, if you know the types involved, you will call the methods directly. If you don't know the types involved, there is no obvious general approach to know whether to use a magic method or not, short of actual measurement. We wind up in this situation because there is existing code that uses It seems that the best way to use this technique effectively is for each magic method to mean exactly one thing. In this case we are using it for two things: On the other hand, if we have a magic method that is specific to |
I'm beginning to think that this issue calls into question the value of
|
I want to note that in 1.15 this issue is going to apply to |
Change https://golang.org/cl/238864 mentions this issue: |
Now that we've added a os.File.ReadFrom method, io.CopyBuffer to a os.File will no longer use the provided buffer. For #16474 For #36817 For #37419 Change-Id: I79a3bf778ff93eab88e88dd9ecbb8c7ea101e868 Reviewed-on: https://go-review.googlesource.com/c/go/+/238864 Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Another report: #56742. |
A related proposal: #58331. |
Change https://go.dev/cl/475575 mentions this issue: |
#66988 is related: Adding |
Another related proposal: #67074 |
Currently, io.CopyBuffer uses WriteTo if the src supports it and ReadFrom if the dst supports it, presumably to avoid an allocation.
The problem is that there exist certain io.ReaderFrom that are not efficient 100% of the time. For example, consider the following implementation of ReadFrom:
This is unfortunate as myWriter satisfies the io.ReaderFrom interface, but it's implementation is no better than io.Copy (always incurring an allocation) in some circumstances.
In the case of io.CopyBuffer, I would argue that the user's expectation is that the buffer passed in is always used. It has the detriment of an extra copy, but I believe the cost of that is much lower than the allocation that is already saved by the user providing the buffer. It main benefit is that it avoids any shenanigans that occur because of inefficient implementations of io.ReaderFrom and io.WriterTo.
Alternative is to do anonymous struct value hack:
But, this hack is not intuitive to most people.
Filed on behalf of @JKJI
The text was updated successfully, but these errors were encountered: