Consider the following benchmark:
func Benchmark(b *testing.B) {
b.ReportAllocs()
var a, z [1000]*flate.Writer
p := sync.Pool{New: func() interface{} { return &flate.Writer{} }}
for i := 0; i < b.N; i++ {
for j := 0; j < len(a); j++ {
a[j] = p.Get().(*flate.Writer)
}
for j := 0; j < len(a); j++ {
p.Put(a[j])
}
a = z
runtime.GC()
}
}
This currently reports: Benchmark-8 10 263962095 ns/op 663587733 B/op 1017 allocs/op
where I find it surprising that there is around 1000 allocations again every cycle. This seems to indicate that the Pool is clearing its entire contents upon every GC. A peek at the implementation seems to indicate that this is so.
If the workload is such that it is cycling through a high number of items, then it makes sense to actually not clear everything. A simple heuristic to accomplish this is to keep track of the number of calls to Pool.Get since the last attempt clearing the pool and ensure that we retain up to that number of items.
\cc @LMMilewski @aclements
Consider the following benchmark:
This currently reports:
Benchmark-8 10 263962095 ns/op 663587733 B/op 1017 allocs/opwhere I find it surprising that there is around 1000 allocations again every cycle. This seems to indicate that the
Poolis clearing its entire contents upon every GC. A peek at the implementation seems to indicate that this is so.If the workload is such that it is cycling through a high number of items, then it makes sense to actually not clear everything. A simple heuristic to accomplish this is to keep track of the number of calls to
Pool.Getsince the last attempt clearing the pool and ensure that we retain up to that number of items.\cc @LMMilewski @aclements