<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Performance on Roberto Selbach</title><link>https://rselbach.com/tags/performance/</link><description>Recent content in Performance 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/performance/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><item><title>Asking the compiler what escapes</title><link>https://rselbach.com/asking-the-compiler-what-escapes/</link><pubDate>Thu, 07 Aug 2014 18:35:00 +0000</pubDate><guid>https://rselbach.com/asking-the-compiler-what-escapes/</guid><description>&lt;p&gt;I was reviewing a parser and found a function returning a pointer to a local variable:&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;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;parseCount&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;s&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;) (&lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;n&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;strconv&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Atoi&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;s&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;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&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:#66d9ef"&gt;nil&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&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:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;n&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;nil&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;My C reflex announced that this must be invalid. Go quite deliberately permits it. The compiler sees that &lt;code&gt;n&lt;/code&gt; must outlive the call and arranges for it to live on the heap. The pointer remains valid; the cost is allocation and later garbage-collection work.&lt;/p&gt;
&lt;p&gt;The useful question was not &amp;ldquo;is taking a local address legal?&amp;rdquo; but &amp;ldquo;where did this value end up, and why?&amp;rdquo; The compiler can explain with optimization diagnostics. With the current toolchain I use a command along these lines:&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;$ go build -gcflags=-m
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./count.go:8: moved to heap: n
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./count.go:8: &amp;amp;n escapes to heap
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The wording changes as the compiler evolves, but the report is far more reliable than guessing from syntax. Taking an address does not always force a heap allocation. If the compiler proves the pointer does not outlive its frame, the value can remain on the stack.&lt;/p&gt;
&lt;p&gt;For example:&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;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;addOne&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;p&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;) { &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;p&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&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;work&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;n&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;addOne&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;n&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;n&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;If &lt;code&gt;addOne&lt;/code&gt; is visible and its parameter does not escape, &lt;code&gt;n&lt;/code&gt; need not move to the heap. Conversely, code without an obvious &lt;code&gt;new&lt;/code&gt; can allocate because a value flows somewhere the analysis cannot bound.&lt;/p&gt;
&lt;p&gt;Interfaces are a common source of surprises. I had a logging helper:&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;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;debug&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;v&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:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Fprintln&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;os&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Stderr&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Passing a local struct through &lt;code&gt;interface{}&lt;/code&gt; and into formatting made it harder for the compiler to prove the lifetime. Formatting itself uses reflection and retains enough generality that values may escape. Replacing the call in a hot parser with a concrete, disabled-by-default trace path removed allocations. I did not replace every &lt;code&gt;fmt&lt;/code&gt; call in the program; human time is also a resource, occasionally.&lt;/p&gt;
&lt;p&gt;Closures can extend lifetimes too:&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;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;counter&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;n&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&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:#66d9ef"&gt;func&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;n&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;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;n&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The returned closure needs &lt;code&gt;n&lt;/code&gt; after &lt;code&gt;counter&lt;/code&gt; returns, so the captured state cannot remain in the departed frame. That is the semantics I asked for. Rewriting it merely to satisfy an allocation count would be silly unless profiles identify this path as important.&lt;/p&gt;
&lt;p&gt;I built a benchmark around two count parsers, one returning &lt;code&gt;*int&lt;/code&gt; and one returning &lt;code&gt;int&lt;/code&gt; plus &lt;code&gt;error&lt;/code&gt;:&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;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;BenchmarkPointer&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;testing&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 style="color:#66d9ef"&gt;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &amp;lt; &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;N&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;i&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:#a6e22e"&gt;p&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;_&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;parseCount&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;42&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;sink&lt;/span&gt; = &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;p&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;BenchmarkValue&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;testing&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 style="color:#66d9ef"&gt;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &amp;lt; &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;N&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;i&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:#a6e22e"&gt;n&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;_&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;parseCountValue&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;42&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;sink&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;n&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The reduced result looked like:&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;BenchmarkPointer-4 20000000 92.1 ns/op 8 B/op 1 allocs/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkValue-4 30000000 55.7 ns/op 0 B/op 0 allocs/op
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I included a package-level sink so the compiler could not discard the result. I also checked the generated diagnostics after changing code; benchmarks without understanding can reward removal of the work being measured.&lt;/p&gt;
&lt;p&gt;Returning the value was the better API anyway. The pointer had been introduced to signal absence, but &lt;code&gt;error&lt;/code&gt; already represented failure and every successful parse had a number. One unnecessary state created one unnecessary allocation.&lt;/p&gt;
&lt;p&gt;Large values complicate the slogan &amp;ldquo;prefer values.&amp;rdquo; Copying a large struct can cost more than indirect access, and a pointer may express shared mutable identity. Escape analysis may also keep a pointer target on the stack when its lifetime is bounded. API semantics come first; diagnostics and benchmarks settle performance questions.&lt;/p&gt;
&lt;p&gt;Stack allocation is attractive because reclaiming it is nearly free when a function returns. Heap allocation requires allocator work and gives the garbage collector another object to trace. Yet avoiding every heap object is neither possible nor desirable in a program with goroutines, closures, shared state, and dynamic data structures.&lt;/p&gt;
&lt;p&gt;Goroutine arguments deserve the same attention. A closure launched with &lt;code&gt;go&lt;/code&gt; can outlive the current function, so captured locals commonly escape. Passing a value as an explicit parameter clarifies what is copied, though it does not guarantee stack allocation when the new goroutine itself needs durable storage. Lifetime, not spelling, drives the decision.&lt;/p&gt;
&lt;p&gt;Compiler boundaries matter too. Analysis is strongest when function bodies and call behavior are visible. Calls through interfaces, reflection, or assembly can force conservative decisions because the compiler cannot prove retention does not occur. A future compiler may prove more, another reason not to encode today&amp;rsquo;s diagnostics as an API religion.&lt;/p&gt;
&lt;p&gt;I keep the compiler version beside benchmark results. An escape decision is an implementation result, not a language guarantee, and a toolchain upgrade can legitimately move the allocation again.&lt;/p&gt;
&lt;p&gt;My current method is deliberately unromantic. I profile to find an allocation-heavy path, reduce it to a benchmark, ask &lt;code&gt;-gcflags=-m&lt;/code&gt; what escapes, and change ownership or API shape if the result remains clear. I do not hunt ampersands by sight. The compiler has a data-flow analysis; I have prejudices from another language. One of those scales better.&lt;/p&gt;</description></item><item><title>What json.Unmarshal really does</title><link>https://rselbach.com/what-json-unmarshal-really-does/</link><pubDate>Tue, 25 Mar 2014 18:25:00 +0000</pubDate><guid>https://rselbach.com/what-json-unmarshal-really-does/</guid><description>&lt;p&gt;I blamed &lt;code&gt;encoding/json&lt;/code&gt; for an allocation spike in an API client. That accusation was directionally correct but not particularly useful. I was asking it to decode into &lt;code&gt;interface{}&lt;/code&gt;, inspect maps, convert numbers, and then copy everything into structs. The package was faithfully performing the work I had requested twice.&lt;/p&gt;
&lt;p&gt;Here was the convenient version:&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;raw&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;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;json&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Unmarshal&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;body&lt;/span&gt;, &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;raw&lt;/span&gt;); &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&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;err&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:#a6e22e"&gt;m&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;raw&lt;/span&gt;.(&lt;span style="color:#66d9ef"&gt;map&lt;/span&gt;[&lt;span style="color:#66d9ef"&gt;string&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:#a6e22e"&gt;id&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; int64(&lt;span style="color:#a6e22e"&gt;m&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;].(&lt;span style="color:#66d9ef"&gt;float64&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;m&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;].(&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Without a concrete destination, JSON objects become &lt;code&gt;map[string]interface{}&lt;/code&gt;, arrays become &lt;code&gt;[]interface{}&lt;/code&gt;, strings become strings, booleans become bools, null becomes nil, and numbers become &lt;code&gt;float64&lt;/code&gt;. Every assertion is another opportunity to panic. Large integer identifiers can also lose precision when represented as floating point.&lt;/p&gt;
&lt;p&gt;A concrete struct gives the decoder much better instructions:&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;type&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;record&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;ID&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int64&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`json:&amp;#34;id&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;Name&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`json:&amp;#34;name&amp;#34;`&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;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;r&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;record&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;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;json&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Unmarshal&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;body&lt;/span&gt;, &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;); &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&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;err&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 decoder scans the input, matches object keys to exported struct fields, allocates strings and nested values as needed, and uses reflection to assign converted values. Struct field metadata is cached internally, so it is not rediscovering every field from first principles on every object. Reflection still has a cost, but my intermediate tree was the larger mistake.&lt;/p&gt;
&lt;p&gt;I measured a reduced case with a 40-element array. The exact numbers depend on machine and revision, but the shape was stable:&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;BenchmarkInterface-4 5000 356000 ns/op 60400 B/op 1012 allocs/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkStruct-4 10000 181000 ns/op 14500 B/op 126 allocs/op
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The benchmark reset the destination each iteration and used &lt;code&gt;testing.B&lt;/code&gt;. It was not a production trace, but it proved that decoding into a generic tree and translating it was expensive enough to stop doing.&lt;/p&gt;
&lt;p&gt;There are a few details behind apparently simple field matching. A &lt;code&gt;json&lt;/code&gt; tag overrides the field name. A tag value of &lt;code&gt;-&lt;/code&gt; ignores the field. Embedded fields participate in selection, with conflicts resolved according to the package&amp;rsquo;s rules. Only exported fields are candidates. Case-insensitive matches are accepted, though I prefer exact wire names because ambiguity is not a feature I need.&lt;/p&gt;
&lt;p&gt;Missing and null are another source of false confidence. If a field is absent, unmarshaling leaves the existing Go value alone. If I reuse a struct, stale data can survive:&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:#a6e22e"&gt;r&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;record&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;ID&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;99&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Name&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;old&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;json&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Unmarshal&lt;/span&gt;([]byte(&lt;span style="color:#e6db74"&gt;`{&amp;#34;name&amp;#34;:&amp;#34;new&amp;#34;}`&lt;/span&gt;), &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Printf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;%+v\n&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The output still contains &lt;code&gt;ID:99&lt;/code&gt;. I decode each independent document into a fresh value unless merging is intentional. For optional scalar fields, a pointer can distinguish missing or null from a present zero only if that distinction is actually part of the application contract.&lt;/p&gt;
&lt;p&gt;For streams, &lt;code&gt;json.Decoder&lt;/code&gt; avoids reading the whole input before decoding:&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:#a6e22e"&gt;dec&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;json&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;NewDecoder&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;resp&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Body&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; {
&lt;/span&gt;&lt;/span&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;r&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;record&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;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dec&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Decode&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;); &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;EOF&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	} &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&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;err&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;consume&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;r&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;It buffers beyond the current value, so code must not assume the underlying reader sits exactly at a JSON boundary afterward. &lt;code&gt;Decoder.UseNumber&lt;/code&gt; is useful when decoding unknown shapes and preserving number text; &lt;code&gt;json.Number&lt;/code&gt; can then be parsed deliberately rather than silently becoming &lt;code&gt;float64&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Custom &lt;code&gt;UnmarshalJSON&lt;/code&gt; methods are the escape hatch for special wire forms. I use them sparingly. They hide work behind ordinary decoding and are easy to make recursive by calling &lt;code&gt;json.Unmarshal&lt;/code&gt; into the same type. An alias type avoids that recursion, but a separate wire struct is often clearer.&lt;/p&gt;
&lt;p&gt;Syntax errors carry an offset, which I include in diagnostics without returning the entire untrusted document. Type errors differ: valid JSON may contain a string where the destination expects an integer. Other fields may already have been assigned when decoding fails, so I treat any error as invalidating the whole destination rather than using a partially filled struct.&lt;/p&gt;
&lt;p&gt;Unknown object fields are ignored by default. That helps forwards-compatible clients but hurts configuration, where a typo should fail. In this Go version I make strict configuration explicit by decoding an object into &lt;code&gt;map[string]json.RawMessage&lt;/code&gt;, deleting recognized keys as I decode them, and rejecting leftovers. &lt;code&gt;RawMessage&lt;/code&gt; also helps when one discriminator selects the concrete shape of a payload. I decode the small envelope first, then decode only its raw payload into the chosen type.&lt;/p&gt;
&lt;p&gt;Decoder convenience does not impose resource limits. Before unmarshaling an HTTP body I bound what I read, and for a stream I bound the number and size of accepted records at the protocol level. A syntactically valid JSON array can still be an excellent memory exhaustion device.&lt;/p&gt;
&lt;p&gt;The direct opinion I took from this exercise is simple: &lt;code&gt;interface{}&lt;/code&gt; is not a schema. If I know the shape of a response, I write it down as Go types. This improves errors, numeric behavior, allocations, and the next reader&amp;rsquo;s chances. &lt;code&gt;encoding/json&lt;/code&gt; was not slow because reflection is cursed. It was slow because I asked for a completely generic representation and then complained that it was completely generic.&lt;/p&gt;</description></item><item><title>Counting allocations in Go 1.1</title><link>https://rselbach.com/counting-allocations-in-go-1-1/</link><pubDate>Fri, 09 Aug 2013 15:46:00 +0000</pubDate><guid>https://rselbach.com/counting-allocations-in-go-1-1/</guid><description>&lt;p&gt;I had two versions of a formatter with nearly identical benchmark times. One looked cleaner, so naturally I distrusted it.&lt;/p&gt;
&lt;p&gt;Go 1.1&amp;rsquo;s benchmark allocation reporting made the difference visible. Benchmarks can call &lt;code&gt;b.ReportAllocs()&lt;/code&gt;, or the test command can request memory statistics for benchmarks. The output includes allocations per operation and bytes allocated per operation alongside timing.&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;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;BenchmarkLabel&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;testing&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 style="color:#a6e22e"&gt;b&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ReportAllocs&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &amp;lt; &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;N&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;i&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:#a6e22e"&gt;_&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;label&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;42&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;My first formatter repeatedly concatenated strings:&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;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;join&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;parts&lt;/span&gt; []&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; {
&lt;/span&gt;&lt;/span&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;out&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;_&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;part&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;range&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;parts&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; &lt;span style="color:#a6e22e"&gt;part&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;As &lt;code&gt;out&lt;/code&gt; grows, concatenation creates new strings and copies prior contents. A &lt;code&gt;bytes.Buffer&lt;/code&gt; version reduced allocation for larger inputs. For two tiny pieces, however, the direct expression remained clearer and entirely adequate. Allocation counts are measurements, not commandments delivered from a heap-shaped mountain.&lt;/p&gt;
&lt;p&gt;The numbers need careful interpretation. “Bytes per operation” is an average over repeated benchmark iterations. Setup performed inside the timed loop counts against the operation, including test data accidentally rebuilt every time. Use &lt;code&gt;b.ResetTimer()&lt;/code&gt; after setup when the setup is not part of what should be measured.&lt;/p&gt;
&lt;p&gt;The benchmark result also depends on escape analysis and compiler decisions. A value that remains on the stack does not appear as a heap allocation merely because source code takes its address. Conversely, converting values to interfaces or retaining pointers can move data to the heap in ways that are not obvious from syntax. Compiler diagnostics can explain candidates after the benchmark shows there is a problem.&lt;/p&gt;
&lt;p&gt;Allocation count and allocated bytes tell different stories. Many tiny objects increase allocator and collector work. One large object can dominate memory traffic while counting as a single allocation. Live heap is different again: an object retained for minutes affects collection even if it was allocated only once.&lt;/p&gt;
&lt;p&gt;I now include allocation reports for benchmarks on hot parsing, formatting, and request paths. I first verify the output cannot be optimized away, keep setup honest, and compare behaviour as well as speed. Reducing allocations often improves performance, but an obscure zero-allocation function can still be a bad bargain.&lt;/p&gt;
&lt;p&gt;I keep the benchmark input representative as well. A formatter tested only with an empty string can truthfully report zero allocations while answering a question no caller was likely to ask.&lt;/p&gt;
&lt;p&gt;In this case the cleaner formatter also allocated less after a small adjustment. I was forced to accept the pleasant result. Software occasionally lacks respect for a good suspicion.&lt;/p&gt;</description></item><item><title>Two small Go 1.1 speedups</title><link>https://rselbach.com/two-small-go-1-1-speedups/</link><pubDate>Tue, 25 Jun 2013 09:57:00 +0000</pubDate><guid>https://rselbach.com/two-small-go-1-1-speedups/</guid><description>&lt;p&gt;I rebuilt a little indexing program with Go 1.1 and it became faster without receiving any of my expert optimization, which is generally the safest kind.&lt;/p&gt;
&lt;p&gt;The program mostly fills maps and then produces enough temporary objects to keep the garbage collector socially engaged:&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;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;_&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;word&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;range&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;words&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;counts&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;word&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Go 1.1 includes a new map implementation and substantial runtime improvements. Map operations are faster, particularly for common key types, and the garbage collector does less work in several important paths. Programs heavy on either can improve simply by recompiling.&lt;/p&gt;
&lt;p&gt;This is not a universal percentage discount. Key sizes, hit rates, map growth, allocation rate, live heap, processor count, and workload shape all matter. My toy index is evidence about my toy index.&lt;/p&gt;
&lt;p&gt;The right comparison is to build the same source with both releases, run multiple times on an otherwise quiet machine, and look at distributions rather than choosing the friendliest result. If the program is a service, latency and pauses may matter more than total runtime.&lt;/p&gt;
&lt;p&gt;Runtime speedups are welcome because they improve ordinary code without making it strange. I will still provide map size hints when I know them and avoid pointless allocation. A faster collector is not a licence to create garbage professionally.&lt;/p&gt;</description></item><item><title>Where did that Go value escape to?</title><link>https://rselbach.com/where-did-that-go-value-escape-to/</link><pubDate>Wed, 14 Nov 2012 15:27:00 +0000</pubDate><guid>https://rselbach.com/where-did-that-go-value-escape-to/</guid><description>&lt;p&gt;I changed a small parser to reuse a helper and made it slower. This was irritating because the new version looked cleaner, performed the same amount of useful work, and did not contain the traditional performance feature known as “a very silly loop.”&lt;/p&gt;
&lt;p&gt;The reduced example looked 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;type&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;point&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;x&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;y&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&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;origin&lt;/span&gt;() &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;point&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;p&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;point&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:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;p&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;Anyone arriving from C may feel a brief alarm here. &lt;code&gt;p&lt;/code&gt; is local, yet the function returns its address. Go makes this safe by deciding where the value must live. The important distinction is not whether the source declares a value inside a function. It is whether the compiler can prove that the value stops being reachable when that function returns.&lt;/p&gt;
&lt;p&gt;If it cannot, the value escapes and is allocated somewhere with a lifetime long enough for its uses, normally the heap. If it can prove the value remains local, it can use the goroutine&amp;rsquo;s stack. This analysis is why Go can permit returning a pointer to a local without turning every local variable into a heap allocation.&lt;/p&gt;
&lt;h2 id="a-small-experiment"&gt;A small experiment&lt;/h2&gt;
&lt;p&gt;I wrote two deliberately uninteresting benchmarks:&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;sink&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;point&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;local&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;p&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;point&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;x&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;y&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;22&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;p&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;x&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;p&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;y&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;escaping&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;p&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;point&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;x&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;y&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;22&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;sink&lt;/span&gt; = &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;p&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;p&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;x&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;p&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;y&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 arithmetic is identical. The assignment to the package variable is not. After &lt;code&gt;escaping&lt;/code&gt; returns, another function can read &lt;code&gt;sink&lt;/code&gt;, so &lt;code&gt;p&lt;/code&gt; must survive. The compiler cannot put it in a dead stack frame and hope nobody notices.&lt;/p&gt;
&lt;p&gt;The compiler can print its decisions. Building with the compiler&amp;rsquo;s escape-analysis diagnostics enabled is considerably more useful than staring at &lt;code&gt;new(point)&lt;/code&gt; and guessing. The exact wording and flags are compiler details and may change, but reports identifying values moved to the heap make a good starting point.&lt;/p&gt;
&lt;p&gt;This matters because source syntax alone is a poor allocation detector. &lt;code&gt;new(T)&lt;/code&gt; does not decree that &lt;code&gt;T&lt;/code&gt; lives on the heap. Returning a value does not decree that a copy must be heap allocated. Conversely, innocent-looking interface conversions, closures, or calls through code the compiler cannot fully inspect may cause values to escape.&lt;/p&gt;
&lt;h2 id="interfaces-and-hidden-addresses"&gt;Interfaces and hidden addresses&lt;/h2&gt;
&lt;p&gt;Suppose I replace a direct call with 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;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;printAny&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;v&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:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Println&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&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;report&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;p&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;point&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;x&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;y&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;22&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;printAny&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;p&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;Putting &lt;code&gt;p&lt;/code&gt; into an interface constructs an interface value containing dynamic type information and the value&amp;rsquo;s data. Depending on what the compiler knows about the call and how that data is represented, it may need storage beyond the current frame. The right answer is not “interfaces allocate.” The right answer is to inspect this particular program with this compiler, then measure it. Blanket rules age badly.&lt;/p&gt;
&lt;p&gt;Closures offer a more obvious case:&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;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;counter&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;n&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&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:#66d9ef"&gt;func&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;n&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;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;n&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The returned function still uses &lt;code&gt;n&lt;/code&gt;, so &lt;code&gt;n&lt;/code&gt; cannot disappear when &lt;code&gt;counter&lt;/code&gt; returns. The compiler arranges storage for the captured variable. This is not a leak. It is the semantics I asked for, delivered with a bill attached.&lt;/p&gt;
&lt;h2 id="stack-is-not-a-moral-achievement"&gt;Stack is not a moral achievement&lt;/h2&gt;
&lt;p&gt;It is tempting to read an escape report as a list of compiler failures. That is not helpful. Heap allocation is necessary whenever data outlives a call, and Go&amp;rsquo;s garbage collector exists precisely because many useful objects do. The report says where the compiler could prove a lifetime, not whether the code is virtuous.&lt;/p&gt;
&lt;p&gt;Still, allocations have costs. Allocating requires runtime work. More live heap data gives the garbage collector more to consider. A short-lived object may be cheap, but a hot loop producing millions of them deserves attention. I use benchmarks to establish that the path matters, allocation diagnostics to locate candidates, and another benchmark to verify the change. Reversing this order produces beautifully optimized code nobody needed.&lt;/p&gt;
&lt;p&gt;Sometimes a small API change keeps a value local. A function can fill a caller-owned value instead of returning a pointer:&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;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;setOrigin&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;p&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;point&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;p&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;x&lt;/span&gt; = &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;p&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;y&lt;/span&gt; = &lt;span style="color:#ae81ff"&gt;0&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;distance&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; {
&lt;/span&gt;&lt;/span&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;p&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;point&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;setOrigin&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;p&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;p&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;x&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;p&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;y&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;Passing an address does not automatically force escape. If the compiler can see that &lt;code&gt;setOrigin&lt;/code&gt; does not retain it, &lt;code&gt;p&lt;/code&gt; may remain on the caller&amp;rsquo;s stack. Again, the analysis follows lifetime, not punctuation.&lt;/p&gt;
&lt;p&gt;I fixed my parser by removing one retained pointer from a frequently called helper. The result allocated less and recovered the lost time. I did not redesign every function to avoid pointers, because that would exchange a measured problem for a readability problem.&lt;/p&gt;
&lt;p&gt;Escape analysis is best treated as an explanation facility. It tells me why the runtime is allocating when a profile says allocation matters. That is much better than the old C technique of declaring stack allocation “fast,” then accidentally returning its address and discovering a more exciting category of performance bug.&lt;/p&gt;</description></item><item><title>Making the Scheduler Show Its Work</title><link>https://rselbach.com/making-the-scheduler-show-its-work/</link><pubDate>Fri, 27 May 2011 20:48:00 +0000</pubDate><guid>https://rselbach.com/making-the-scheduler-show-its-work/</guid><description>&lt;p&gt;Scheduler discussions become folklore quickly, so I built a few experiments that force observable events instead of inferring everything from total runtime.&lt;/p&gt;
&lt;p&gt;The first launches workers that announce when they start, wait on a gate, perform fixed CPU work, and announce completion. A channel carries each announcement to one logger goroutine. That gives me a trace without concurrent writes making the output resemble ransom typography.&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;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;worker&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;gate&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;-&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;chan&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;events&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;chan&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Event&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;events&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Event&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;ready&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;gate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;burnCPU&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;events&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Event&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;done&amp;#34;&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;I run the same executable with different &lt;code&gt;GOMAXPROCS&lt;/code&gt; values. With one processor thread allowed, CPU-bound completion is serialized even though all workers are runnable. With two, completions overlap in wall time on my dual-core machine. More than two adds runnable work but no additional cores, and eventually loses to scheduling overhead.&lt;/p&gt;
&lt;p&gt;The second experiment replaces CPU work with pipe reads. Many goroutines can wait for I/O while the runtime arranges for other work to proceed. Increasing the processor allowance barely changes throughput because the external producer is the limit. This is why one benchmark cannot characterize “goroutine performance.”&lt;/p&gt;
&lt;p&gt;The third experiment is intentionally rude: one goroutine runs a tight arithmetic loop with no communication while another tries to report periodically. On this early runtime, the reporter can be delayed noticeably. Scheduling behavior is under active development, and I should not assume the preemption properties of a mature operating-system scheduler.&lt;/p&gt;
&lt;p&gt;Adding explicit communication points makes the test cooperative and reflects real pipeline code better. I do not sprinkle meaningless calls into production loops merely to influence today&amp;rsquo;s runtime; I break large work into units with natural channel operations or function calls, then measure again.&lt;/p&gt;
&lt;p&gt;The event channel affects the experiment, of course. Observing a scheduler changes timing by adding synchronization. I keep messages outside the inner loop and compare against an uninstrumented wall-clock run. The trace explains ordering; it does not provide cycle-accurate truth.&lt;/p&gt;
&lt;p&gt;These results belong to the May 2011 snapshot and my machine. They are not a specification of future Go scheduling, and I am deliberately avoiding an internal diagram that will expire before the ink dries. The practical findings are modest: concurrency is not parallelism, blocking and CPU work stress different paths, and processor settings should follow measurements. Schedulers are quite willing to show their work, but only if the experiment asks a specific question.&lt;/p&gt;</description></item><item><title>Cache Lines Beat Clever Loops</title><link>https://rselbach.com/cache-lines-beat-clever-loops/</link><pubDate>Thu, 03 Mar 2011 21:31:00 +0000</pubDate><guid>https://rselbach.com/cache-lines-beat-clever-loops/</guid><description>&lt;p&gt;I translated a matrix filter from C to Go and then spent an afternoon optimizing arithmetic that was not the main cost. The profile pointed at memory movement, so I reduced the problem to traversal order.&lt;/p&gt;
&lt;p&gt;The matrix is stored in one slice in row-major order. These loops calculate the same sum:&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;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;y&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;y&lt;/span&gt; &amp;lt; &lt;span style="color:#a6e22e"&gt;height&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;y&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;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;x&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;x&lt;/span&gt; &amp;lt; &lt;span style="color:#a6e22e"&gt;width&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;x&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:#a6e22e"&gt;sum&lt;/span&gt; &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pixels&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;y&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;width&lt;/span&gt;&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;x&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;/code&gt;&lt;/pre&gt;&lt;/div&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;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;x&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;x&lt;/span&gt; &amp;lt; &lt;span style="color:#a6e22e"&gt;width&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;x&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;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;y&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;y&lt;/span&gt; &amp;lt; &lt;span style="color:#a6e22e"&gt;height&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;y&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:#a6e22e"&gt;sum&lt;/span&gt; &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pixels&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;y&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;width&lt;/span&gt;&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;x&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first walks adjacent elements. The second jumps by an entire row. On a large image the row-wise loop is much faster because each fetched cache line supplies several values used immediately. The column-wise loop asks the processor to fetch many lines and uses a small part of each before moving on.&lt;/p&gt;
&lt;p&gt;Bounds checks and multiplication were my original suspects. Hoisting the row offset made the code clearer and helped a little:&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;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;y&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;y&lt;/span&gt; &amp;lt; &lt;span style="color:#a6e22e"&gt;height&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;y&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:#a6e22e"&gt;row&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pixels&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;y&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;width&lt;/span&gt;:(&lt;span style="color:#a6e22e"&gt;y&lt;/span&gt;&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)&lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;width&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;x&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;x&lt;/span&gt; &amp;lt; &lt;span style="color:#a6e22e"&gt;width&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;x&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:#a6e22e"&gt;sum&lt;/span&gt; &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;row&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;x&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But traversal order remained the large effect. A more sophisticated arithmetic trick could not compensate for unfriendly access.&lt;/p&gt;
&lt;p&gt;I benchmark with enough data to exceed the small caches, repeat the operation, and verify the sum so the compiler cannot discard the work. These are 2011 compiler snapshots, so absolute timings and optimization behavior are temporary facts. The memory hierarchy is less temporary.&lt;/p&gt;
&lt;p&gt;I alternate test order as well. Running one version first every time can give it consistently colder data and turn benchmark order into a hidden input.&lt;/p&gt;
&lt;p&gt;This also affects data structure choice. A slice of small structures may have better locality than a slice of pointers to separately allocated structures. The latter adds pointer chasing and gives the allocator freedom to scatter objects. It may still be right when identity and independent lifetimes matter, but it is not free.&lt;/p&gt;
&lt;p&gt;My practical conclusion is embarrassingly physical: visit data in the order it lies in memory. Before unrolling loops or translating a bit trick from C, inspect the access pattern. The processor is very fast when fed and becomes an expensive space heater when asked to wait.&lt;/p&gt;</description></item><item><title>One Thread, Two Threads</title><link>https://rselbach.com/one-thread-two-threads/</link><pubDate>Tue, 14 Dec 2010 17:58:00 +0000</pubDate><guid>https://rselbach.com/one-thread-two-threads/</guid><description>&lt;p&gt;I ran two independent CPU loops in goroutines and expected my dual-core machine to halve the time. It did not. Expectations remain the cheapest profiler available and the least accurate.&lt;/p&gt;
&lt;p&gt;This runtime defaults to limited processor use. Setting &lt;code&gt;GOMAXPROCS=2&lt;/code&gt; for the experiment allowed Go work to execute on two operating-system threads:&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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ time GOMAXPROCS=1 ./spin
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;real 0m3.84s
&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;$ time GOMAXPROCS=2 ./spin
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;real 0m2.17s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The result is not exactly half. Both loops touch memory, the scheduler has work, and the rest of the machine has declined to disappear for my benchmark.&lt;/p&gt;
&lt;p&gt;Goroutines and parallel execution are separate ideas. Thousands of goroutines can make a concurrent design clear while one thread runs them. Increasing the thread allowance can provide parallelism when there is independent CPU work and hardware to execute it.&lt;/p&gt;
&lt;p&gt;The early scheduler also has rough edges. A tight loop with no calls may delay other goroutines more than I expect from mature thread schedulers. I add real synchronization rather than relying on hopeful fairness, and I test the weekly runtime I intend to use.&lt;/p&gt;
&lt;p&gt;For I/O-heavy programs, two processor threads may change little. For CPU loops, it can help until memory bandwidth or coordination dominates. The variable is a knob, not a turbo button. Turbo buttons at least had a satisfying light.&lt;/p&gt;</description></item><item><title>Counting Bits Like the Kernel</title><link>https://rselbach.com/counting-bits-like-the-kernel/</link><pubDate>Tue, 09 Nov 2010 20:39:00 +0000</pubDate><guid>https://rselbach.com/counting-bits-like-the-kernel/</guid><description>&lt;p&gt;Kernel code contains algorithms that look unnecessarily peculiar until the constraints are visible. Population count, the number of set bits in a machine word, is a good example.&lt;/p&gt;
&lt;p&gt;The obvious C version examines every bit:&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-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;count_bits&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;long&lt;/span&gt; word)
&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;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; n &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&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;while&lt;/span&gt; (word) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; n &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; word &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; word &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&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; n;
&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;This performs one iteration for every bit position up to the highest set bit. A sparse bitmap still pays for all the zeros in between.&lt;/p&gt;
&lt;p&gt;Brian Kernighan&amp;rsquo;s familiar operation removes the lowest set bit at each iteration:&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-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;count_bits&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;long&lt;/span&gt; word)
&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;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; n &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&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;while&lt;/span&gt; (word) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; word &lt;span style="color:#f92672"&gt;&amp;amp;=&lt;/span&gt; word &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; n&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&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; n;
&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;Subtracting one flips the lowest set bit to zero and turns lower zeros into ones. The &lt;code&gt;&amp;amp;&lt;/code&gt; keeps everything above that bit and clears the changed suffix. The loop therefore runs once per set bit.&lt;/p&gt;
&lt;p&gt;That is excellent for sparse words and less impressive for dense ones. Kernels also use table lookups and architecture-specific instructions where available. A byte table has predictable work but adds memory access; a processor instruction can win decisively but needs dispatch or compile-time selection. There is no universally fastest source fragment independent of data and machine.&lt;/p&gt;
&lt;p&gt;Word width matters too. Kernel types and helpers make that choice explicit, while a casual userspace benchmark can accidentally compare different widths and announce a victory produced by less work.&lt;/p&gt;
&lt;p&gt;I benchmarked the two loops over generated sparse and dense arrays, keeping generation outside the timed section. As expected, the clear-lowest-bit version dominated sparse input. Dense input narrowed the gap considerably. Repeating one constant word produced lovely numbers and measured the compiler more than the routine, so I stopped doing that.&lt;/p&gt;
&lt;p&gt;The broader lesson from kernel work is to understand the representation before admiring the trick. Bitmaps pack state compactly and permit word-at-a-time operations, but contention, cache placement, and scan direction can matter more than shaving an instruction from population count.&lt;/p&gt;
&lt;p&gt;I like this algorithm because its mechanism fits in one sentence and its performance caveat requires another. Most optimization stories should have both sentences. The ones with only the first usually end in a benchmark that accidentally proves the author&amp;rsquo;s laptop exists.&lt;/p&gt;</description></item><item><title>The Slice Is Not the Array</title><link>https://rselbach.com/the-slice-is-not-the-array/</link><pubDate>Thu, 04 Nov 2010 21:04:00 +0000</pubDate><guid>https://rselbach.com/the-slice-is-not-the-array/</guid><description>&lt;p&gt;Coming from C, I first read a Go slice as a pointer with nicer indexing. That model works until &lt;code&gt;append&lt;/code&gt; allocates a new backing array or a tiny subslice keeps a large allocation alive. The missing piece is that a slice is a small descriptor, not the array itself.&lt;/p&gt;
&lt;p&gt;Conceptually, the descriptor contains three items:&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-text" data-lang="text"&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;| pointer | length | capacity |
&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; v
&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;| 10 | 20 | 30 | 40 | 50 |
&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 pointer identifies an element in a backing array. Length controls the elements currently accessible through indexing. Capacity describes how far the slice may grow from that starting point before another array is needed. This is a conceptual layout, not a promise that user code should depend on runtime internals.&lt;/p&gt;
&lt;p&gt;Given an array and a slice:&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:#a6e22e"&gt;a&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; [&lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;]&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;{&lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;40&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;s&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;a&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;3&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;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Println&lt;/span&gt;(len(&lt;span style="color:#a6e22e"&gt;s&lt;/span&gt;), cap(&lt;span style="color:#a6e22e"&gt;s&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;my snapshot prints:&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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2 4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The slice sees &lt;code&gt;20&lt;/code&gt; and &lt;code&gt;30&lt;/code&gt;, and it has capacity through the end of &lt;code&gt;a&lt;/code&gt;. Assigning &lt;code&gt;s[0] = 99&lt;/code&gt; changes &lt;code&gt;a[1]&lt;/code&gt; because both views refer to the same storage. Passing &lt;code&gt;s&lt;/code&gt; to a function copies the descriptor, but both descriptors still point at that storage.&lt;/p&gt;
&lt;p&gt;This explains a function that appears not to resize its caller:&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;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;grow&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;s&lt;/span&gt; []&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;s&lt;/span&gt; = append(&lt;span style="color:#a6e22e"&gt;s&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;60&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 local descriptor changes. The caller&amp;rsquo;s descriptor does not. If the append fits within capacity, the backing array may contain the new element, but the caller&amp;rsquo;s length still excludes it. If it does not fit, append obtains another backing array and the local slice points there. The useful pattern is to return the result:&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;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;grow&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;s&lt;/span&gt; []&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;) []&lt;span style="color:#66d9ef"&gt;int&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; append(&lt;span style="color:#a6e22e"&gt;s&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;60&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:#a6e22e"&gt;s&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;grow&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;s&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The same mechanism makes append performance less mysterious. Growing one element at a time does not normally allocate an array on every call. Capacity grows in larger steps and existing elements are copied when storage changes. The exact growth policy belongs to this experimental runtime and should not become an application assumption.&lt;/p&gt;
&lt;p&gt;I verified allocations indirectly by timing construction with and without an initial capacity:&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:#a6e22e"&gt;a&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; make([]&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;100000&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &amp;lt; &lt;span style="color:#ae81ff"&gt;100000&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;i&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:#a6e22e"&gt;a&lt;/span&gt; = append(&lt;span style="color:#a6e22e"&gt;a&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;i&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;When the final size is known, reserving capacity avoids repeated growth and copying. When it is not known, append&amp;rsquo;s policy is generally better than inventing my own allocation scheme. Capacity is a performance hint with semantic consequences, not a target to maximize.&lt;/p&gt;
&lt;p&gt;Subslice lifetime is the less obvious consequence. I wrote a reader that loaded a large file, found one short token, and returned a slice containing that token. The token was only a few bytes, but its descriptor still pointed into the large backing array. As long as the small slice remained reachable, the collector could not reclaim the array.&lt;/p&gt;
&lt;p&gt;The fix was to copy the useful bytes into a new, correctly sized slice:&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:#a6e22e"&gt;token&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; make([]&lt;span style="color:#66d9ef"&gt;byte&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;end&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;start&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;copy(&lt;span style="color:#a6e22e"&gt;token&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;start&lt;/span&gt;:&lt;span style="color:#a6e22e"&gt;end&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;token&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Copying sounds wasteful until the alternative is retaining several megabytes for a twelve-byte identifier. Profiling the whole process settled that argument quickly.&lt;/p&gt;
&lt;p&gt;Overlapping slices deserve similar care. &lt;code&gt;copy&lt;/code&gt; is the operation intended for moving slice contents, including overlap supported by the implementation. Hand-written forward loops can overwrite input before reading it. This is one of those cases where code that looks closer to C is merely closer to a C bug.&lt;/p&gt;
&lt;p&gt;Slices also make zero values useful. A nil slice has length and capacity zero, can be ranged over, and can be appended to. For many producers there is no need to allocate an empty slice eagerly. An empty non-nil slice may still matter at an encoding boundary, but inside the program I prefer the simpler zero value unless behavior says otherwise.&lt;/p&gt;
&lt;p&gt;Arrays remain values with their length in the type. Assigning an array copies its elements, and &lt;code&gt;[4]int&lt;/code&gt; differs from &lt;code&gt;[5]int&lt;/code&gt;. Slices provide the flexible view normally wanted by functions. Remembering that distinction prevents a great deal of accidental copying and aliasing.&lt;/p&gt;
&lt;p&gt;All of this describes the November 2010 toolchain I am using. Syntax, built-ins, and growth details may change before a stable release. The useful mental model is simpler: a slice is a copied descriptor over shared storage. Ask which descriptor changes, which array it references, and who keeps that array alive. That usually finds the surprise.&lt;/p&gt;</description></item><item><title>Profiling the Program I Wrote</title><link>https://rselbach.com/profiling-the-program-i-wrote/</link><pubDate>Thu, 19 Aug 2010 19:19:00 +0000</pubDate><guid>https://rselbach.com/profiling-the-program-i-wrote/</guid><description>&lt;p&gt;I was certain my parser spent its time converting decimal numbers. Certainty is a useful indication that I should profile before touching anything.&lt;/p&gt;
&lt;p&gt;The tools include a real-time sampling profiler named &lt;code&gt;6prof&lt;/code&gt;. Despite the name, it also understands the other supported architectures. The interface is still moving, so this is a note about the weekly release on my desk rather than scripture.&lt;/p&gt;
&lt;p&gt;I let the profiler start the real program:&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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ 6prof ./logsum
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 46.8% bytes.(*Buffer).Write
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 21.1% runtime.memmove
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 8.7% parseNumber
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On Linux/amd64 I can ask it to write pprof data, provided the program was linked with &lt;code&gt;6l -e&lt;/code&gt;:&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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ 6prof -P cpu.prof ./logsum
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The exact output format varies, but the result was unambiguous. My “efficient” reporting path repeatedly grew a byte buffer. Number parsing was visible and not remotely the first problem.&lt;/p&gt;
&lt;p&gt;Preallocating a reasonable output buffer removed most growth and copies. Runtime dropped by roughly a third on the same input. Replacing the decimal parser with a clever version afterward produced noise-sized improvement, so I reverted it. Clever code that cannot beat measurement is just decorative risk.&lt;/p&gt;
&lt;p&gt;Sampling has limits. A short run may not gather enough samples, and compiler optimization can make line attribution odd. This profiler samples threads while they are running, asleep, or waiting for I/O, so a hot entry may be telling me about waiting rather than arithmetic. I feed several seconds of representative data and repeat runs. I also keep wall-clock timing around the complete operation because users do not experience percentages.&lt;/p&gt;
&lt;p&gt;Input matters just as much. A profile from the tiny sample used by unit tests mostly measures startup and can confidently direct optimization toward irrelevant code. I use a captured workload large enough to reach steady behavior.&lt;/p&gt;
&lt;p&gt;The young runtime itself appears in profiles. Garbage collection, allocation, copying, and scheduler work are part of the program&amp;rsquo;s cost, even if I did not type those function names. They should not automatically be dismissed as profiler clutter. Often they point back to an allocation pattern I control.&lt;/p&gt;
&lt;p&gt;Now I reproduce, time, profile, change one thing, and time again. This lacks the emotional satisfaction of immediately rewriting the function I dislike. Annoyingly, it makes the program faster.&lt;/p&gt;</description></item><item><title>cgo Is a Border, Not a Bridge</title><link>https://rselbach.com/cgo-is-a-border-not-a-bridge/</link><pubDate>Fri, 11 Jun 2010 20:52:00 +0000</pubDate><guid>https://rselbach.com/cgo-is-a-border-not-a-bridge/</guid><description>&lt;p&gt;I have too much useful C code to pretend a new language arrives on an empty disk. My first serious Go experiment therefore needed an old image-processing library. That led directly to cgo, then to several hours learning that a foreign-function interface is a border crossing, not a transparent bridge.&lt;/p&gt;
&lt;p&gt;The smallest call is almost suspiciously simple. In the snapshot I used, a preamble before &lt;code&gt;import &amp;quot;C&amp;quot;&lt;/code&gt; declares the C side:&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:#f92672"&gt;package&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;scale&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:#75715e"&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#include &amp;lt;stdint.h&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;static uint32_t clamp(uint32_t v, uint32_t limit) {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; return v &amp;lt; limit ? v : limit;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;C&amp;#34;&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;clamp&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;v&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;limit&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;uint32&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;uint32&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; uint32(&lt;span style="color:#a6e22e"&gt;C&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;clamp&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;C&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;uint32_t&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;v&lt;/span&gt;), &lt;span style="color:#a6e22e"&gt;C&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;uint32_t&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;limit&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 exact generated names and build steps are tied to this weekly release. cgo is changing quickly, so this is a field note rather than a durable manual. I run cgo as part of the package build, compile its generated C, then let the normal architecture-specific tools finish the package.&lt;/p&gt;
&lt;p&gt;Scalar conversion is the easy part. Buffers force the real questions: who owns the memory, how long does an address remain valid, and may either runtime retain it after the call? I settled on a strict rule for this experiment: Go calls C synchronously, C consumes the supplied bytes before returning, and C stores no pointer into Go-managed memory.&lt;/p&gt;
&lt;p&gt;For output allocated by the C library, I expose a C release function and call it with &lt;code&gt;defer&lt;/code&gt; immediately after checking the returned pointer. That keeps allocation and release in the same ownership domain. Wrapping a C allocation in a Go slice may avoid a copy, but it also makes lifetime assumptions much easier to hide. I copy unless profiling proves the copy matters.&lt;/p&gt;
&lt;p&gt;Strings are another ownership test disguised as convenience. A Go string is not a NUL-terminated C string. Conversion may allocate. Embedded zero bytes have C semantics whether I find them philosophically agreeable or not. Passing length explicitly is better whenever the C API permits it.&lt;/p&gt;
&lt;p&gt;Then I measured the crossing. I called a trivial C function in a loop and compared it with the same arithmetic written in Go. The absolute figures are not worth publishing because both cgo and the compiler are moving, but the shape was obvious: crossing for every pixel was disastrous; crossing once per image was lost in the actual work.&lt;/p&gt;
&lt;p&gt;That changed my wrapper. The first version exposed the C library nearly function for function:&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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Go pixel loop -&amp;gt; cgo -&amp;gt; C operation -&amp;gt; return
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The revised version sends a whole row or image:&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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Go request -&amp;gt; cgo -&amp;gt; C loop over image -&amp;gt; return
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The second interface is less “complete” and much more useful. It minimizes transitions and expresses the operation at the level where ownership is clear.&lt;/p&gt;
&lt;p&gt;Callbacks in the other direction are harder. They combine scheduler state, foreign threads, and object lifetime. I avoided them by collecting results in C and returning a batch. That is not always possible, but it is a better starting point than assuming a C callback can enter arbitrary Go code safely. The runtime is young and its constraints deserve respect, not exploratory production outages.&lt;/p&gt;
&lt;p&gt;Failures need translation too. The C library reports integer status codes and sometimes leaves detail in a structure. My wrapper converts those into a small Go value implementing the snapshot&amp;rsquo;s &lt;code&gt;os.Error&lt;/code&gt; convention. I do not leak C constants throughout callers. If the library returns a partial result, the wrapper states whether that result remains valid and who frees it.&lt;/p&gt;
&lt;p&gt;The build is the least elegant part. Header search paths, linker flags, and platform differences have not vanished; they have merely moved to the package boundary. This is still better than spreading them through the program, but cgo does not make a C dependency portable by blessing it with a comment.&lt;/p&gt;
&lt;p&gt;I keep one integration test that allocates, calls, copies, releases, and repeats. It catches ownership regressions more reliably than staring harder at the wrapper.&lt;/p&gt;
&lt;p&gt;My conclusion is deliberately conservative. cgo is excellent for preserving a tested, substantial C implementation. It is poor as a shortcut for tiny functions that cross the boundary constantly. Design a coarse interface, keep ownership explicit, translate failures once, and benchmark the complete operation. A border can support trade. It should not run through the middle of the kitchen.&lt;/p&gt;</description></item><item><title>perf Turns Counters Into Questions</title><link>https://rselbach.com/perf-turns-counters-into-questions/</link><pubDate>Tue, 08 Dec 2009 18:53:00 +0000</pubDate><guid>https://rselbach.com/perf-turns-counters-into-questions/</guid><description>&lt;p&gt;I had two versions of a parser. One finished faster, so I declared victory and nearly deleted the slower one. Before doing that, I tried the new perf tools included with recent kernels and discovered that my explanation for the improvement was wrong.&lt;/p&gt;
&lt;p&gt;The kernel&amp;rsquo;s performance-counter infrastructure provides a common way to measure hardware and software events. The &lt;code&gt;perf&lt;/code&gt; tool can count events for a command, sample execution and report where those samples landed. Instead of beginning with a profiler tied to one processor model, I can ask through one kernel interface and use the events available on this machine.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;perf stat&lt;/code&gt; is a useful first question. It reports elapsed time along with counts such as cycles, instructions, context switches and page faults. Ratios matter more than isolated totals. Instructions per cycle can suggest whether the processor is retiring useful work efficiently, while cache-related events may explain why an apparently smaller algorithm still stalls.&lt;/p&gt;
&lt;p&gt;My faster parser did not execute dramatically fewer instructions. It incurred fewer cache misses because its data was laid out more compactly. I had credited a clever branch change that happened nearby. The benchmark result was real; my story about it was fan fiction.&lt;/p&gt;
&lt;p&gt;Sampling answers a different question. &lt;code&gt;perf record&lt;/code&gt; periodically captures the current instruction pointer, and &lt;code&gt;perf report&lt;/code&gt; aggregates samples by symbol. With suitable symbols, hot functions become visible without instrumenting every call. Sampling has overhead and statistical uncertainty, but it is often much less disruptive than logging entry and exit around a hot path.&lt;/p&gt;
&lt;p&gt;Counters are constrained resources. The processor can measure only a limited number simultaneously, so events may be multiplexed. Some events are model-specific, and virtualized or restricted environments may expose less. A cache-miss count without knowing which cache or how the event is defined is an attractive number with an uncertain biography.&lt;/p&gt;
&lt;p&gt;I also avoid optimizing from one profile. Workload, input size, compiler options and machine state all matter. I run repeated measurements, preserve the input and compare complete behavior. A ten-percent gain in a microbenchmark is not useful if the changed layout doubles memory for the real service.&lt;/p&gt;
&lt;p&gt;I strongly prefer starting performance work with &lt;code&gt;perf stat&lt;/code&gt;, then sampling when the totals suggest a question. ftrace remains better for many scheduling and kernel-flow investigations; perf is especially convenient for connecting program hotspots to processor and kernel counters. Neither tool replaces understanding, but both are considerably more reliable than staring at source until one loop begins to look guilty.&lt;/p&gt;</description></item><item><title>The Allocator Is Part of the Program</title><link>https://rselbach.com/the-allocator-is-part-of-the-program/</link><pubDate>Thu, 21 May 2009 21:08:00 +0000</pubDate><guid>https://rselbach.com/the-allocator-is-part-of-the-program/</guid><description>&lt;p&gt;A multithreaded parser scaled nicely to two workers and then barely improved. I inspected locks in my code and found no obvious villain. Eventually a profile pointed at allocation and deallocation, which felt unfair because &lt;code&gt;malloc()&lt;/code&gt; had not appeared on my list of interesting algorithms.&lt;/p&gt;
&lt;p&gt;An allocator manages more than a pointer advancing through memory. It finds suitably sized free regions, records metadata, returns memory to reusable pools and sometimes asks the operating system for more pages. In multiple threads it must also protect shared structures. A program creating millions of short-lived objects can therefore spend substantial time contending inside code it never explicitly called.&lt;/p&gt;
&lt;p&gt;The system allocator already uses techniques to reduce this cost. Implementations may maintain size classes, bins and multiple arenas so not every allocation takes one global lock. Alternative allocators such as tcmalloc and jemalloc make different tradeoffs involving per-thread caches, fragmentation and concurrency. Swapping one in can be an excellent experiment, but it is not a substitute for understanding the allocation pattern.&lt;/p&gt;
&lt;p&gt;My parser allocated a small object for every token, then freed the whole tree after processing a request. The lifetimes were almost identical. A simple region allocator fit better: reserve larger blocks, hand out aligned pieces by moving a pointer, and release the region at once when the request completes. Individual deallocation disappears because the ownership model says nothing outlives the request.&lt;/p&gt;
&lt;p&gt;This improved both speed and clarity. The gain did not come from a magical allocator; it came from expressing lifetime in the data structure. It also introduced a strict rule: pointers into the region cannot escape. Violating that rule turns cleanup into a mass dangling-pointer production line, which is efficient in the wrong direction.&lt;/p&gt;
&lt;p&gt;Object pools are useful when objects have uniform shape and repeated lifetimes, but I use them cautiously. A pool retains memory, complicates construction and may conceal an unbounded queue. Per-thread caches can reduce contention while increasing total memory consumption. Peak resident size matters as much as allocations per second on a shared machine.&lt;/p&gt;
&lt;p&gt;Measurement must include realistic concurrency and duration. A short benchmark can reward an allocator for keeping every page. A long-running service eventually reveals fragmentation and cache growth. I watch CPU profiles, allocation counts and resident memory rather than treating one throughput number as a complete biography.&lt;/p&gt;
&lt;p&gt;I prefer changing ownership and allocation frequency before replacing the general allocator. When patterns are genuinely general and contention remains measurable, testing another mature allocator is reasonable. In this case, the region was the stronger result because it made the program&amp;rsquo;s lifetime visible. I had gone looking for a faster &lt;code&gt;malloc()&lt;/code&gt; and found that asking for less was cheaper.&lt;/p&gt;</description></item><item><title>Zero Copy Is Mostly Fewer Copies</title><link>https://rselbach.com/zero-copy-is-mostly-fewer-copies/</link><pubDate>Fri, 24 Apr 2009 18:37:00 +0000</pubDate><guid>https://rselbach.com/zero-copy-is-mostly-fewer-copies/</guid><description>&lt;p&gt;I wrote a file server in the most direct way possible: read a block into a user-space buffer, then write that block to the socket. The code was simple and, for small files, entirely satisfactory. With large transfers, the process spent an impressive amount of CPU moving bytes it never inspected.&lt;/p&gt;
&lt;p&gt;The ordinary path copies file data from the kernel&amp;rsquo;s page cache into my buffer and then back into kernel-managed socket buffers. It also crosses the system-call boundary repeatedly. The data needs to travel from disk or cache to the network device, but my temporary ownership adds work without adding information.&lt;/p&gt;
&lt;p&gt;Linux offers &lt;code&gt;sendfile()&lt;/code&gt; for this case. It transfers data between a file descriptor and a socket while keeping the payload out of the application&amp;rsquo;s address space. The kernel can connect the file and networking paths more directly, reducing copies and context transitions. &amp;ldquo;Zero copy&amp;rdquo; is convenient shorthand, not a sworn statement that no hardware or kernel component ever copies a byte.&lt;/p&gt;
&lt;p&gt;The loop still has to handle partial progress. &lt;code&gt;sendfile()&lt;/code&gt; can transfer fewer bytes than requested, be interrupted, or return &lt;code&gt;EAGAIN&lt;/code&gt; for a nonblocking socket. I keep an offset and remaining length in the connection state, request another writable notification when necessary, and continue later. Replacing &lt;code&gt;read()&lt;/code&gt; and &lt;code&gt;write()&lt;/code&gt; with one call does not replace flow control.&lt;/p&gt;
&lt;p&gt;Headers complicate matters slightly. A protocol header may be generated in memory while the body comes from a file. I can send the small header normally and use &lt;code&gt;sendfile()&lt;/code&gt; for the payload. Trying to eliminate the final tiny copy at all costs generally produces more cleverness than performance.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;splice()&lt;/code&gt; is a more general Linux mechanism for moving data between descriptors through a pipe, and can connect paths that &lt;code&gt;sendfile()&lt;/code&gt; does not cover. It also adds another set of constraints and bookkeeping. For serving regular files, &lt;code&gt;sendfile()&lt;/code&gt; expresses the intention clearly, so that is where I start.&lt;/p&gt;
&lt;p&gt;There are cases where a user-space buffer belongs in the path. Compression, encryption in user space, content transformation and checksums may require examining the bytes. Small responses may show no measurable benefit. Portability also matters; this is an operating-system facility, not ISO C acquiring telepathy.&lt;/p&gt;
&lt;p&gt;I strongly prefer the kernel-assisted path when the application is merely forwarding an unchanged large file. The preference comes after profiling, not before. In my test it lowered CPU use while maintaining throughput, which leaves the processor available for actual work. The original loop remains easier to explain, but explaining why a server copies every byte twice is not especially satisfying.&lt;/p&gt;</description></item><item><title>Idle Host, Busy Guest</title><link>https://rselbach.com/idle-host-busy-guest/</link><pubDate>Wed, 25 Jun 2008 20:45:00 +0000</pubDate><guid>https://rselbach.com/idle-host-busy-guest/</guid><description>&lt;p&gt;My laptop stayed warm with an apparently idle KVM guest running. The guest reported nearly no processor use, and the host showed only small bursts, so I first blamed inaccurate temperature sensors. Sensors are convenient suspects because they cannot write rebuttals.&lt;/p&gt;
&lt;p&gt;I stopped services in the guest one by one. A periodic timer user was waking it frequently, but even a minimal guest produced more activity than expected. Virtual timer interrupts, emulated devices and host-side handling all require exits from guest execution. An idle guest is therefore not necessarily an idle host.&lt;/p&gt;
&lt;p&gt;Tickless operation helps when the guest kernel can avoid unnecessary periodic ticks and when KVM presents timers accurately enough for longer idle periods. It cannot eliminate device emulation or a program requesting frequent wakeups. The host must also be tickless while idle; saving ticks in one layer and generating them in the other is elaborate bookkeeping, not power management.&lt;/p&gt;
&lt;p&gt;After using a tickless guest kernel and disabling a polling monitor, host wakeups dropped and the machine cooled. Timekeeping remained the thing to watch: aggressive experiments are worthless if the guest clock drifts or timers fire late.&lt;/p&gt;
&lt;p&gt;My conclusion is to shut down unused guests on battery and to measure host wakeups, not merely guest CPU percentage. For a guest that must remain available, remove polling and use appropriate virtual devices where supported. Virtualization can make a machine appear to be doing nothing in two operating systems simultaneously while the processor is, with admirable professionalism, doing work for both.&lt;/p&gt;</description></item><item><title>CFS After the First Week</title><link>https://rselbach.com/cfs-after-the-first-week/</link><pubDate>Sun, 28 Oct 2007 17:35:00 +0000</pubDate><guid>https://rselbach.com/cfs-after-the-first-week/</guid><description>&lt;p&gt;I upgraded my workstation to Linux 2.6.23 mainly to try the Completely Fair Scheduler. The machine compiles code, plays music, runs a browser with an unreasonable number of pages, and occasionally hosts a virtual machine. Under a large parallel build, the old setup could make switching desktops feel as though the keyboard had been sent by post.&lt;/p&gt;
&lt;p&gt;My first test was not clever. I started a build, dragged windows around and declared the new scheduler smoother. Then I rebooted into the older kernel and it also seemed smooth. Human perception is a wonderful measurement instrument if the desired unit is confidence.&lt;/p&gt;
&lt;p&gt;For a better comparison, I repeated the same build with the same compiler jobs, timed it, and introduced an interactive task that periodically needed a small amount of processor time. Total build time changed little. The interesting difference was the delay before the interactive task ran when all cores were busy. Large stalls became less frequent with 2.6.23, though disk activity could still make the desktop miserable for entirely different reasons.&lt;/p&gt;
&lt;p&gt;CFS approaches scheduling without the old collection of active and expired priority arrays. It tracks each runnable task&amp;rsquo;s virtual runtime, an adjusted measure of how much processor service that task has received. The task with the smallest virtual runtime is the one most entitled to run next. Runnable tasks are kept in a red-black tree, making that leftmost choice efficient as tasks arrive, block and wake.&lt;/p&gt;
&lt;p&gt;The word &amp;ldquo;fair&amp;rdquo; needs care. Equal nice levels should receive roughly equal shares over time; it does not mean every task receives an identical slice at every instant. Nice values weight the virtual runtime, so a task with lower priority accumulates entitlement differently and receives a smaller share. Sleeper behavior emerges because a task that blocks stops consuming processor time. When it wakes, it may be behind its competitors and therefore run promptly, within limits intended to prevent absurd advantages.&lt;/p&gt;
&lt;p&gt;My naive mental model was that CFS simply gives tiny slices to interactive programs and large slices to compilers. That is not how it identifies interactivity. There is no reliable label saying &amp;ldquo;this is the music player.&amp;rdquo; Instead, the accounting rewards tasks that use little processor time and wake to perform brief work. A music player, terminal or window manager often behaves that way, but so can a less noble process.&lt;/p&gt;
&lt;p&gt;The scheduler also has to balance two competing costs. Switching too often improves apparent responsiveness but loses time to context switches and cache disruption. Waiting too long lets a runnable task monopolise a processor. CFS uses a target scheduling latency and derives slices from the number and weights of runnable tasks, subject to a minimum granularity. As the run queue grows, it cannot promise that every task runs within the same tiny interval without turning the processor into a context-switching demonstration.&lt;/p&gt;
&lt;p&gt;This explains one of my results. With a modest build load, interactive delays stayed short. With a deliberately ridiculous number of runnable jobs, latency grew again. Fair scheduling cannot manufacture processor time. It can distribute scarcity coherently, which is less exciting but more useful.&lt;/p&gt;
&lt;p&gt;Another misleading result came from I/O. During linking, the desktop paused even though CFS was selecting processor tasks correctly. The disk queue was saturated and applications blocked waiting for files. Changing the CPU scheduler did not cure that. Likewise, swapping overwhelmed every subtle scheduler difference. Before blaming CFS, I now check whether the delayed task is actually runnable rather than sleeping on storage.&lt;/p&gt;
&lt;p&gt;Nice levels behaved more predictably in my tests than before, but group fairness remains a practical concern. If one user starts one processor-bound task and another starts twenty, scheduling every task independently can grant the second user most of the machine. There is work around grouping tasks, but the default desktop case still mostly exposes per-task fairness. The name should not be read as a constitutional guarantee.&lt;/p&gt;
&lt;p&gt;I also tried changing scheduler tunables because numbers invite interference. Reducing latency made window feedback slightly sharper under synthetic load and increased context switches. Increasing it helped throughput by an amount too small to distinguish confidently while making the terminal occasionally sticky. The defaults survived my experiments, an outcome that wounded my pride but spared my configuration files.&lt;/p&gt;
&lt;p&gt;After a week, I am keeping 2.6.23. The improvement is not that compilations finish magically sooner. They do not. The improvement is that short, waking tasks more often get service without elaborate interactivity heuristics, and nice values map onto a cleaner accounting model. That makes a loaded workstation feel less capricious.&lt;/p&gt;
&lt;p&gt;The practical conclusion is to test CFS with repeatable mixed workloads, not window wiggling alone. Separate CPU delay from disk delay, compare distributions of latency rather than one lucky run, and resist tuning until a real workload demonstrates a problem. CFS is a substantial scheduler change, not a performance patch that makes every number smaller. On my machine it makes contention more orderly. Given what computers usually do under pressure, orderly is a fairly ambitious achievement.&lt;/p&gt;</description></item><item><title>Tickless Is Not Sleepless</title><link>https://rselbach.com/tickless-is-not-sleepless/</link><pubDate>Mon, 10 Sep 2007 18:45:00 +0000</pubDate><guid>https://rselbach.com/tickless-is-not-sleepless/</guid><description>&lt;p&gt;I built a recent kernel with tickless idle enabled because the laptop fan had begun running while the machine did apparently nothing. I expected the new kernel to become silent by virtue of one configuration option. It did not. The fan is apparently not impressed by release notes.&lt;/p&gt;
&lt;p&gt;My naive test was to leave a terminal open, wait a minute, and compare the battery estimate. That number wandered by nearly an hour between readings. I then watched processor usage, which stayed close to zero while the fan continued. Neither test told me what was waking the processor.&lt;/p&gt;
&lt;p&gt;The periodic timer tick normally interrupts each processor at a fixed frequency so the kernel can account for time and perform scheduling work. During idle periods, those interrupts wake an otherwise sleeping processor for very little purpose. Tickless idle programs the next timer event instead, allowing a longer uninterrupted sleep. It cannot help if an application or driver requests a timer every few milliseconds.&lt;/p&gt;
&lt;p&gt;On my machine, an application polling for status updates was doing exactly that. Closing it reduced wakeups dramatically and the fan eventually slowed. A network driver also produced regular activity when the interface was unused. Disabling the interface for a test separated that problem from the timer configuration.&lt;/p&gt;
&lt;p&gt;Measuring several idle intervals also mattered. One short sample could be dominated by mail checking, disk flushing or my own impatient mouse movement.&lt;/p&gt;
&lt;p&gt;There are limits to the name. Tickless does not mean the kernel never uses timer interrupts, and the current work mainly avoids ticks while a processor is idle. A busy processor still needs scheduling and accounting. Hardware timer support matters too; unreliable timers can turn a power improvement into missed timeouts or clocks that drift.&lt;/p&gt;
&lt;p&gt;The practical lesson is that tickless operation removes one systematic source of wakeups. It does not forgive programs that poll constantly, nor drivers that chatter with hardware for entertainment. I am keeping it enabled because idle residency improved and battery measurements over several complete runs are better. But the useful debugging question is now &amp;ldquo;who wakes the processor?&amp;rdquo; rather than &amp;ldquo;is CPU usage low?&amp;rdquo; Zero percent can be composed of thousands of tiny interruptions, which is a very computer-like definition of resting.&lt;/p&gt;</description></item></channel></rss>