Send me an email!
What's happening in Go tip (2014-01-10)
It’s 2014, we’ve survived Christmas and New Year’s Eve and the Go team has begun work on the next release. And thus the What’s happening in Go tip series has started again.
For this iteration of the series, there will be some small adjustments. The main change will be the lack of a strict weekly cycle. There might be multiple articles a week, or none. This is partly due to personal reasons and partly because it allows a more flexible approach to reporting changes. In the same vein, individual articles might be shorter than before, for the sake of addressing new developments faster.
The second change is the addition of coverage of proposals and discussions that haven’t seen any code changes yet. This is due to the fact that Go 1.3 will see some big changes (primarily the planned rewrite of the compilers in Go) that should be covered early on.
In this issue of the series we will, however, concentrate on the new
sync.Pool
type since it’s the first major addition to the standard
library in Go 1.3.
What’s happening
Addition of the sync.Pool
type
Relevant CLs: CL 41860043, CL 43990043, CL 37720047, CL 44080043, CL 44150043, CL 44060044 CL 44050044, CL 44680043, CL 46010043
Projects like the JVM invest a lot of time and effort in improving their garbage collectors to battle the high amount of garbage they need to deal with. Go, on the other hand, generally takes the approach of avoiding garbage in the first place, requiring a less sophisticated garbage collector by giving control back to the programmer.
To this end, several parts of the standard library have pools of
reusable objects. The regexp
package maintains state machines for
concurrent use of a regexp, the fmt
package has printers and other
packages have their own pools as well, or would like to have them.
This approach, however, has two issues. The obvious one is code duplication: all packages have to implement their own pools, even though the implementation often is identical. A subtler issue is the lack of draining the pools. These simple implementations never release memory, which defies the point of a garbage collected language and can lead to unnecessarily high memory usage.
Because of this, Brad Fitzpatrick
suggested adding
a public Cache
type to the sync
package. This suggestion triggered
a lot of discussion. Should Go provide such a type in the standard
library or should it instead be private? Should it actually evict
items, and if so, when? Should it even be named Cache
, or rather
Pool
?
Let me explain the difference between a cache and a pool, and why it
is important for this discussion. The type that Brad Fitzpatrick
requested is actually a pool: A set of interchangeable values where it
doesn’t matter which concrete value you get out, because they’re all
identical. You wouldn’t even notice when, instead of getting a value
from the pool, you get a newly created one. Caches, on the other hand,
map keys to concrete values. A prominent example are disk caches,
where files from a slow storage medium are cached in the system’s main
memory for faster access. If the cache contains values for the keys
(in this example file names) A
and B
, and you request the value
associated with A
, you definitely do not want to get B
out. The
fact that values in a cache aren’t identical also adds a lot more
complexity to their eviction policies, namely the question of which
values to evict when. The
Wikipedia article on cache algorithms
lists 13 different algorithms, from the well known LRU cache to more
complex ones like the
LIRS caching algorithm.
With that out of the way, the only real question concerning our pool is when to drain it. And pretty much every possibility has been suggested: somewhen before GC, somewhen after GC, clock-based or by employing weak pointers. And all suggestions had their own drawbacks.
After a lot of back and forth, Russ Cox eventually
suggested an API and draining policy
that was very simple: Drain the pools during garbage collection. This
proposal reminds us of the fact that the purpose of the Pool
type is
to reuse memory between garbage collections. It should not circumvent
garbage collection, it should only make it more efficient.
Brad implemented said proposal in CL 41860043 and, after some more
back and forth, the CL was committed to the Go repository. Of course
this CL wasn’t the end of the story. First, all existing pools had to
be replaced with sync.Pool
.
This happened in the CLs CL 43990043, CL 37720047, CL 44080043, CL 44150043, CL 44060044 and not in CL 44050044. CL 44050044 was
special in that it tried to use sync.Pool
in the encoding/gob
package, which has local free lists. And local is the keyword. The
pools in encoding/gob
belong to instances of a Decoder
and are
both short-lived and already handled appropriately by the GC. A free
list would exist as long as the decoder did, and then be thrown away.
Russ Cox
commented on the CL,
making clear what the purpose of sync.Pool
is, and what it isn’t. To
this end, Rob Pike submitted and committed CL 44680043, which expands
the type’s documentation to state its purpose more clearly.
Pool’s intended use is for free lists maintained in global variables, typically accessed by multiple goroutines simultaneously. Using a Pool instead of a custom free list allows the runtime to reclaim entries from the pool when it makes sense to do so. An appropriate use of sync.Pool is to create a pool of temporary buffers shared between independent clients of a global resource. On the other hand, if a free list is maintained as part of an object used only by a single client and freed when the client completes, implementing that free list as a Pool is not appropriate.
From the comment (and prior discussions) it’s also apparent that the
addition of sync.Pool
is tentative and might be reverted before the
release of Go 1.3, if it doesn’t live up to its promises. This is also
tracked by
Issue 6984.
While this marks the end of this exposition on sync.Pool
, it’s not
the end of the pool’s journey yet. There’s still CL 46010043, which
improves the very simple initial implementation to become better
suited for concurrent use, but it hasn’t been reviewed yet at this
point.
Small change to the development process
Beginning with the Go 1.3 cycle, some small adjustments have been made to the development process. These changes only affect people who are either directly involved in the development process or who, like me, follow changes closely.
A new mailing list, golang-codereviews, has been created and made the default Cc target of new CLs, instead of the old golang-dev. The idea behind this is to reduce the noise on golang-dev, to allow actual conversations.
At the same time, a new dashboard has been created, allowing committers to easily track open issues and CLs. Anyone who is interested in how the Go team works is encouraged to read the usage instructions for the new dashboard.