-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
compress/lzw
encoders maintain a rather large table (64k) mapping uncompressed bytes to their compressed bits. Because the encoder requires a Close
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 with image/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:
name old time/op new time/op delta
Encoder/1e4-8 108µs ± 1% 109µs ± 2% +1.25% (p=0.008 n=9+10)
Encoder/1e5-8 1.06ms ± 2% 1.11ms ± 2% +4.15% (p=0.000 n=10+9)
Encoder/1e6-8 10.5ms ± 2% 11.0ms ± 2% +4.37% (p=0.000 n=10+9)
name old speed new speed delta
Encoder/1e4-8 93.0MB/s ± 1% 91.8MB/s ± 2% -1.23% (p=0.008 n=9+10)
Encoder/1e5-8 94.1MB/s ± 2% 90.4MB/s ± 2% -3.98% (p=0.000 n=10+9)
Encoder/1e6-8 95.2MB/s ± 2% 91.3MB/s ± 2% -4.18% (p=0.000 n=10+9)
name old alloc/op new alloc/op delta
Encoder/1e4-8 77.9kB ± 0% 4.4kB ± 0% -94.35% (p=0.000 n=10+9)
Encoder/1e5-8 77.9kB ± 0% 4.4kB ± 0% -94.33% (p=0.000 n=10+10)
Encoder/1e6-8 77.9kB ± 0% 5.0kB ± 0% -93.60% (p=0.000 n=10+10)
name old allocs/op new allocs/op delta
Encoder/1e4-8 3.00 ± 0% 4.00 ± 0% +33.33% (p=0.000 n=10+10)
Encoder/1e5-8 3.00 ± 0% 4.00 ± 0% +33.33% (p=0.000 n=10+10)
Encoder/1e6-8 3.00 ± 0% 4.00 ± 0% +33.33% (p=0.000 n=10+10)
(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?