<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Sync on Roberto Selbach</title><link>https://rselbach.com/tags/sync/</link><description>Recent content in Sync on Roberto Selbach</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Thu, 18 Sep 2014 16:25:00 +0000</lastBuildDate><atom:link href="https://rselbach.com/tags/sync/index.xml" rel="self" type="application/rss+xml"/><item><title>sync.Pool is not a cache</title><link>https://rselbach.com/sync-pool-is-not-a-cache/</link><pubDate>Thu, 18 Sep 2014 16:25:00 +0000</pubDate><guid>https://rselbach.com/sync-pool-is-not-a-cache/</guid><description>&lt;p&gt;I used Go 1.3&amp;rsquo;s new &lt;code&gt;sync.Pool&lt;/code&gt; to reuse temporary buffers in an HTTP formatter. Then I almost used it to cache parsed templates. The first idea reduced garbage; the second would have randomly forgotten application data.&lt;/p&gt;
&lt;p&gt;A pool holds temporary values that may be removed at any time. Garbage collection is explicitly allowed to clear it. That makes it suitable for amortizing allocation of scratch objects, not for storing anything whose presence affects correctness.&lt;/p&gt;
&lt;p&gt;My buffer pool looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;buffers&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;sync&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Pool&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;New&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt;{} {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; new(&lt;span style="color:#a6e22e"&gt;bytes&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Buffer&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;render&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;v&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;) []&lt;span style="color:#66d9ef"&gt;byte&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;b&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;buffers&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Get&lt;/span&gt;().(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;bytes&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Buffer&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Reset&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;writeValue&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;v&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;out&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; append([]byte(&lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;), &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Bytes&lt;/span&gt;()&lt;span style="color:#f92672"&gt;...&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Len&lt;/span&gt;() &amp;lt; &lt;span style="color:#ae81ff"&gt;64&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;10&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Reset&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;buffers&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Put&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;out&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The copy before &lt;code&gt;Put&lt;/code&gt; is essential. &lt;code&gt;b.Bytes()&lt;/code&gt; aliases the buffer&amp;rsquo;s storage. Returning that slice and putting the buffer back would allow another goroutine to overwrite the caller&amp;rsquo;s result. Pool safety does not transfer ownership safety to pooled objects.&lt;/p&gt;
&lt;p&gt;The pool synchronizes &lt;code&gt;Get&lt;/code&gt; and &lt;code&gt;Put&lt;/code&gt;, but an object obtained from it belongs to the caller until returned. Two goroutines must not use the same buffer concurrently. Go 1.3&amp;rsquo;s &lt;code&gt;bytes.Buffer&lt;/code&gt; does not expose its capacity, so I use the rendered length as a simple retention policy: a response that grows a buffer past 64 KiB does not return that buffer to the pool. It is only a proxy for capacity, but it keeps a freshly oversized allocation out of the pool.&lt;/p&gt;
&lt;p&gt;In a benchmark of the formatter, pooling reduced allocation volume:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkRender-4 500000 4100 ns/op 2304 B/op 12 allocs/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkRenderPool-4 500000 3000 ns/op 768 B/op 5 allocs/op
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The output copy accounts for necessary ownership. If the function wrote directly to an &lt;code&gt;io.Writer&lt;/code&gt;, even that copy could disappear, probably a better API than ever more pooling.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;New&lt;/code&gt; should return the expected type consistently. A pool may omit &lt;code&gt;New&lt;/code&gt;, in which case &lt;code&gt;Get&lt;/code&gt; can return nil and every caller must handle that. I define it when all callers need the same scratch type; this keeps allocation policy together without pretending values are permanent.&lt;/p&gt;
&lt;p&gt;Copying a &lt;code&gt;sync.Pool&lt;/code&gt; after first use is invalid because its synchronization state and local storage are not value-like application data. I keep pools as package variables or pointer-owned fields initialized once.&lt;/p&gt;
&lt;p&gt;I would not add &lt;code&gt;sync.Pool&lt;/code&gt; without allocation profiles and a benchmark. It obscures object lifetime and may provide little benefit when allocations are already short and cheap. A cache promises retrieval; a pool merely offers to rummage in a box the collector is free to empty.&lt;/p&gt;</description></item></channel></rss>