-
Notifications
You must be signed in to change notification settings - Fork 17.5k
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
strings: optimize WriteTo to use an intermediate buffer for large strings #13848
Comments
I don't think we can do this for compatibility reasons. I think we're stuck with the implicit guarantee that 1 WriteString call means 1 Write call. |
Is it possible to address the types that don't have WriteString instead? On Thu, 7 Jan 2016, 11:20 Brad Fitzpatrick notifications@github.com wrote:
|
Where did this implicit guarantee come from? The documentation for WriteString is: // WriteString writes the contents of the string s to w, which accepts a slice of bytes.
// If w implements a WriteString method, it is invoked directly. If this guarantee does exist, it seems important enough that it should be documented. EDIT: |
My test implementation does exactly that; it only does intermediate buffering for non stringWriters. |
I meant add WriteString to the types that you need. On Thu, 7 Jan 2016, 11:29 Joe Tsai notifications@github.com wrote:
|
It's implicit because it's not written down. If it were documented, it would be explicit. :-) As some sort of precedent, https://golang.org/pkg/log/#Logger says "Each logging operation makes a single call to the Writer's Write method" https://golang.org/pkg/fmt/#Fprint and friends don't document a single Write call, but they do guarantee it. But yes, perhaps we could document it. |
I see. I can definitely see how the single Write is important in console applications and possibly others. I filed this ticket because I noticed large allocations due to writings strings and when I saw io.WriteString, my first thought was "oh sweet, here's a library method that'll efficiently write a string for me!" Alas, I was wrong. In theory, another way to do what I wanted would be: io.Copy(w, strings.NewReader(s)) But... that doesn't work well either, since strings.Reader just calls io.WriteString, and we're back to the same problem. What are your thoughts on doing the following:
|
Both SGTM |
CL https://golang.org/cl/19754 mentions this issue. |
After seeing https://golang.org/cl/19754 I'm not really a fan of this. I left some comments there, but let's discuss here. Unfortunately I don't have a proposal other than just not doing anything. |
When performance optimizing a write of a large string without WriteString, this may be a reasonable set of solutions:
|
I submitted a different PR, for something that I personally use in my code, it shows a major improvement for
|
CL https://golang.org/cl/19722 mentions this issue. |
Copy of my comment CL/19722 (which uses unsafe to directly cast a string as a []byte in io.WriteString): As much as I love seeing performance improve, I object against this because it breaks memory safety. In an ideal world, it probably would be okay to directly convert a string to a []byte. However, this CL relies on the fact that every implementation of io.Writer perfectly obeys the documented rules that the Writer never mutates the input slice. Reality is, I have seen buggy Writers that did alter the input for short period (yes, that's wrong), but this change would make the presence of those types of Writers worse. I certainly don't think this should be done at the level of the standard level. I am aware of this method used in other packages, like CockroadDB. But, at least in their use, they have a firm understanding that the concrete underlying method does not alter the input. However, io.WriteString cannot make that assumption safely. As a secondary argument against it, there's a warning in the reflect package against the use of StringHeader or SliceHeader for portability reason. I think it be wise to respect those warnings. |
@dsnet, if io.Copy doesn't work for you,
What about: io.Copy(w, struct { io.Reader }{ strings.NewReader(s) }) That should work, no? |
That would work, but there are some detriments:
In regard to your comments on CL/19754:
|
Using
go1.5
NOTE: This issue used to be about using an intermediate buffer in io.WriteString, but will instead perform the optimization in strings.Reader.WriteTo instead. The description in this issue still refers to WriteString, but the performance numbers will probably be the same once applied to strings.Reader.WriteTo.
Currently WriteString does
w.Write([]byte(s))
ifw
is not astringWriter
. This causes a memory allocation proportional to len(s). Instead, we should use an intermediate buffer for large strings.Using this test code:
We can get the number of bytes allocated to be capped at some maximum:
I was pleasantly surprised that the runtime also decreased, but this may be because the small buffer fits entirely in the cache.
The text was updated successfully, but these errors were encountered: