-
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
compress/lzw: add Resetter interface to allow encoders to be reused #26535
Comments
Pools have subtle properties that affect their performance (see #23199 and #22950). While #23199 isn't an issue here since the object is fixed size, #22950 can have adverse affect in all usages of pools in certain workloads. That being said, why do you say:
Is that because the API returns |
A pool seems like a poor fit for this use-case, since the A |
Thanks for the insight, a There's also the question of One option would be for Maybe 64k garbage is okay for an image encoder, in which case the |
Stumbled on this while looking at some heap profiles generated from using https://github.com/hashicorp/memberlist. Adding the |
Change https://golang.org/cl/273667 mentions this issue: |
We clearly did a bad job with all the compress APIs. We should not have returned interfaces from the constructors. For example we got zlib.NewWriter correct - it returns a *zlib.Writer. But we got zlib.Reader wrong - it returns an io.ReadCloser. When we wanted to add a Reset method to the underlying reader, we had no way to expose it, so we added the zlib.Resetter interface with a guarantee that the resulting io.ReadCloser also implemented zlib.Resetter. That's being proposed here for lzw, but on the Writer side, not the Reader side. It bothers me a bit that flate.Resetter and zlib.Resetter are both Read-side Resetters while this would be a Write-side Resetter. What happens when we want to Reset the lzw reader as well? An alternative to defining these new interfaces would be to define the actual concrete type structs - *lzw.Reader and *lzw.Writer - and then document that the results from lzw.NewReader and lzw.NewWriter are guaranteed to be type-assert-able to those actual concrete types. Then any new methods we need could be added there without any new interfaces, and in particular without a different interface for reading and writing and a different interface for each new method. Any thoughts about that alternative (defining the Reader and Writer structs for the package and documenting that the interfaces can be type-asserted to them, and then adding Reset as struct methods)? |
/cc @nigeltao |
This proposal has been added to the active column of the proposals project |
For completeness, I can imagine two other (potentially poor) approaches:
|
It's true that if we have structs that can be declared and initialized (by the Reset method) they would stand alone.
Any objections to this approach? |
The asymmetry with |
@rsc why not return a *Writer and guarantee that it always fulfills WriteCloser? That would be compatible and usable without type assertion? |
@andig Would this technically violate the compatibility guarantee, since a function variable/argument typed for the current signature would no longer accept the new signature? E.g.:
would break under this proposed type signature. |
Guess it would 😣 |
Based on the discussion above, this proposal seems like a likely accept. |
While playing around with this I noticed that NewReader/NewWriter currently return a separate struct ( |
@rsc - Just wanted to clarify a small detail on the Reset methods: do we want to pass the order and litWidth to It seems like there is some inconsistency amongst the various Reset APIs on this one.
|
In my opinion, it was a mistake for |
To @dsnet's point, removing these parameters from |
LGTM. |
To answer @agnivade's comment, it sounds like @dsnet and @nigeltao agree to include all the options in Reset, as in the comment above. |
No change in consensus, so accepted. 🎉 |
I will update my CL to accomodate the changes in the proposal. |
Change https://golang.org/cl/323273 mentions this issue: |
For #26535 For #44513 For #46005 Change-Id: I70d3711ab6451a61b526abb3da8e91243f637656 Reviewed-on: https://go-review.googlesource.com/c/go/+/323273 Trust: Ian Lance Taylor <[email protected]> Reviewed-by: Joe Tsai <[email protected]>
compress/lzw
encoders maintain a rather large table (64k) mapping uncompressed bytes to their compressed bits. Because the encoder requires aClose
per stream they cannot be reused for multiple streams. This means that lzw creates 64k of garbage per use, which is especially apparent when encoding animation withimage/gif
.While we can't easily change the API in go1 to support reuse, we can greatly reduce garbage by pooling the
table
parameter of encoders. I prototyped this behavior and got the following benchmarks:(Note that these are on top of my existing CL https://go-review.googlesource.com/c/go/+/123478 that improves time/op significantly)
I don't see much use of pools in the standard library, is this not generally a good approach? Even if it is, is the memory overhead worth it for the 4% performance increase?
The text was updated successfully, but these errors were encountered: