sync.Pool and slices with no known upper size bound

1,682 views
Skip to first unread message

Jsor

unread,
Jun 19, 2014, 9:07:11 AM6/19/14
to golan...@googlegroups.com
I think sync.Pool is a pretty good addition to Go, but it seems to me it's intended to handle constant-sized structs, or at least slices with a known upper bound in size. Since Get returns "an arbitrary element" it makes it insufficient for slice pools, which necessarily have a concept of "fit". Generally a slice pool with a Get request cannot return a slice smaller than the one required, but can return a bigger one downsliced to the appropriate len. The problem with using sync.Pool for slices is that, even if you don't make a possibly ill-conceived attempt to optimize for "tightest fit" (find the slice with the absolute smallest cap that fits the requested size within some max delta), you still have to call Get an unknown number of times before you get a slice big enough for your request; potentially exhausting the entire pool before you realize you need to make a new one.

I looked at pool.go and the code seems rather involved with things like goroutine pinning, and is integrated with the runtime which may make custom implementations difficult. I was wondering if it were possible to have a reasonably fast/good implementation that takes into account slice sizing. Or is such a thing ill advised for various reasons I don't understand? Is there a better way to maintain a slice pool?

alexru...@gmail.com

unread,
Jun 19, 2014, 10:08:46 AM6/19/14
to golan...@googlegroups.com
You can use multiple pools, each one for some capacity range.

четверг, 19 июня 2014 г., 16:07:11 UTC+3 пользователь Jsor написал:

Carlos Castillo

unread,
Jun 19, 2014, 2:49:57 PM6/19/14
to golan...@googlegroups.com
Slices don't make good items in sync.Pool's since it's very easy to share them, or more specifically the backing array. This makes it easy to unknowingly write code with difficult to find concurrency bugs.

eg:
a := []int{1,2,3,4,5}
b := a[2:3] // 'b' references the same array as 'a' now
pool.Put(a)
// stuff happens, someone calls pool.Get to retrieve 'a'
b[0] = 5 // Uh-oh, I've modified someone else's memory.

Jeff Juozapaitis

unread,
Jun 19, 2014, 2:58:28 PM6/19/14
to Carlos Castillo, golang-nuts
That's true, but I think that's more of a Know What You're Doing™ thing, if you're never doing any arbitrary in-the-middle reslicing or appending (or at best -- only making reslices in disjoint chunks) then it's not really a huge concern and pools can benefit you.

I do think multiple pools is a good idea. Make all slices in your internal/unexported code to have a cap of <n> where <n> fits some rule (e.g. powers of 2, multiples of 10, whatever). As long as you're within that chunk, you can reslice up to the cap, if you're going to exceed the cap, return the slice to the pool and grab one from the pool of the appropriate size.


--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/9qhH8JQRcl8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Alberto García Hierro

unread,
Jun 20, 2014, 3:26:05 AM6/20/14
to golan...@googlegroups.com
First and foremost, don't use pools for slices that you might return to the user, only for those that you're using internally, because slices are mutable.
The best solution we've found is setting a maximum pooled slice size which covers a high percentage of the cases and just ignoring the pool when the
slice grows past that size.

You can see a very short and concise example at https://github.com/rainycape/unidecode/blob/master/unidecode.go (note the re-slicing at line 40, after getting from the pool).

Regards,
Alberto

Dmitry Vyukov

unread,
Jun 20, 2014, 1:39:34 PM6/20/14
to Alberto García Hierro, golang-nuts
On Fri, Jun 20, 2014 at 12:26 AM, Alberto García Hierro
<alb...@garciahierro.com> wrote:
> First and foremost, don't use pools for slices that you might return to the
> user, only for those that you're using internally, because slices are
> mutable.

All objects in Go are mutable. Slices are not special.
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an

Gustavo Niemeyer

unread,
Jun 20, 2014, 2:32:06 PM6/20/14
to Jsor, golan...@googlegroups.com
There are a few ways to solve that problem, but I would start with the
simplest possible one: ignore the size altogether.. just pick an item
out of the pool, grow it as needed, and put it back when done. This
will organically grow the backing arrays as necessary. From there, if
really necessary, I'd attempt allocating power-of-two-sized slices,
and finally using separate pools with known power-of-two-sized slices
each.

Of course, it's important to carefully measure. It's way too easy to
improperly assume the impact.
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-nuts...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.



--

gustavo @ http://niemeyer.net

Alberto García Hierro

unread,
Jun 21, 2014, 5:20:08 AM6/21/14
to golan...@googlegroups.com, alb...@garciahierro.com


On Friday, June 20, 2014 7:39:34 PM UTC+2, Dmitry Vyukov wrote:


All objects in Go are mutable. Slices are not special.


Except strings :-). But I mentioned slices specifically because that's was he was asking about and it's rather easy for newcomers to forget that re-slicing generates a new slice with the same backing array and modifications to this new slice will affect the old one (and viceversa).

Regards,
Alberto

Dmitry Vyukov

unread,
Jun 21, 2014, 12:09:42 PM6/21/14
to Alberto García Hierro, golang-nuts
On Sat, Jun 21, 2014 at 2:20 AM, Alberto García Hierro
<alb...@garciahierro.com> wrote:
>
>
> On Friday, June 20, 2014 7:39:34 PM UTC+2, Dmitry Vyukov wrote:
>>
>>
>>
>> All objects in Go are mutable. Slices are not special.
>>
>
> Except strings :-)

strings are as mutable as ints, that is -- mutable


> But I mentioned slices specifically because that's was
> he was asking about and it's rather easy for newcomers to forget that
> re-slicing generates a new slice with the same backing array and
> modifications to this new slice will affect the old one (and viceversa).
>
> Regards,
> Alberto
>
Reply all
Reply to author
Forward
0 new messages