-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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
cmd/compile: optimize Write([]byte(stringVal)) to not copy the string #18822
Comments
I feel like there was a dup bug with the same plan but rejected because as I recall: nobody should be working with such large strings and stay in bytes if they want performance. Was this motivated by something concrete? |
There was no specific motivation; it was just an idea I had. |
Your compiler annotation for Such an (internal) annotation would have additional benefits:
|
This would remove the need for my attempted optimization in #13848, which tried to optimize string writing at the standard library level. |
How does this work for nested calls of func (t *T) Write(p []byte) (int, error) {
return t.wr.Write(p)
} You do not know if |
@dsnet For a case like that you turn |
@ianlancetaylor, Doesn't that approach fail for this example (that nobody should ever write): t := &T{wr: ...}
t.Write([]byte("hello"))
func (t *T) Write(p []byte) (int, error) {
n, err := t.wr.Write(p) // Assume t.wr.Write mutates p, but not known at compile time
fmt.Println(p[0]) // Read p, expect to see mutated value
return n, err
} So I guess that this is solvable if |
You're right: in general, if the method does something with the buffer after passing it to |
Avoiding the allocation is exactly the reason we have a separate
WriteString interface.
I think we should figure out the question of immutable types in general
before adding this kind of workarounds.
(I read that it's on Russ' plan for 2017 to research more about
immutability in Go.)
BTW, we probably don't need to make the compiler work so hard for this, one
intermediate step that is probably good enough would be to allow trivial
and allocate free WriteString implemented as:
func (t *T) WriteString(s string) (int, error) {
return t.Write([]byte(s))
}
when the compiler could statically prove that the Write method won't write
to the string.
|
Related to #2205 (cmd/compile: read-only escape analysis and avoiding string <-> []byte copies). |
I create a thread on #golang-dev to discuss some possible optimizations for this. |
It would be nice to optimize
Write([]byte(stringVal))
to not copy the string value. This is normally safe because mostWrite
methods do not modify the byte slice passed in. In fact, the documentation forio.Writer
requires that theWrite
method not modify the byte slice, although this is not (and can not be) enforced.Here is how we can do this.
For each function and method that takes a parameter of slice type, record whether the slice's underlying array is modified. This will require generating annotations similar to the ones we generate for escape analysis. Assembly functions will require an annotation similar to //go:noescape. (Naturally, a slice that escapes must be treated as modified.)
For each call of the form
F([]byte(stringVal))
, where we know thatF
does not modify the slice, we can pass the string value directly. This would do essentially the same thing as the existing optimization for map lookups in which a[]byte
is converted to astring
. This fixes direct calls, but of course the interesting cases all involve calls to methods of values of typeio.Writer
.For any type with a
Write
method that does modify the slice, generate at compile time an additionalWrite·2
method that makes a copy of the slice and then calls theWrite
method. The method is namedWrite·2
to ensure that it does not conflict with any user written method.When converting any type to
io.Writer
(or any interface type that inherits fromio.Writer
), check for the presence of aWrite·2
method. If that method exists, add it to the interface as an additional entry in the itab.For any call to an interface method
Write([]byte(stringVal))
, modify the call to call a special functiondoWrite
in the io package, without copying the string.doWrite
will check for aWrite·2
method, and call it if it exists; that will cause the string to be copied as happens today. If theWrite·2
method does not exist, which will be the normal case,doWrite
will call theWrite
method as usual, knowing that theWrite
method does not modify the slice.Generalizing this to other methods is left as an exercise for the reader.
The text was updated successfully, but these errors were encountered: