Go Channel Pools

Pools are typically employed when objects are expensive to initialize or have a meaningful impact on garbage collection. I recently worked on a project that used a number of Otto runtimes, each with a great deal of initialization that I did not want to pay for needlessly.

Go already has the sync.Pool library which supports a very simple approach to object pools: retain objects until the next GC cycle. As the Go runtime advances, these cycles can be run very frequently. As a result, most people now consider sync.Pool to be pointless.

A better approach I have found is to retain objects in a buffered channel. The approach is quite simple:

  1. Create a channel large enough to hold a reasonable number of references to your objects.
  2. When you need an instance of an object in the code, you call a special function which will either selectively dequeue an object from the channel or, if there are none available, just create a new one.
  3. When you no longer require an instance of an object, you call another function to retire the instance. If there is space on the channel to do so, it is selectively enqueued and made available for later use, or if there is no space in the channel it is just deleted.

By counting the instances in which we cannot effectively utilize the channel, we can get a better idea over time how large the channel has to be.

This is all made possible by combining Go channels and select.

See a full example here:

last update 2018-01-15