ELI5 - bytes.buffer

xuanbao · · 493 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Yo guys</p> <p>I&#39;ve been trying to search the interwebs for some resources on how bytes.buffer works within golang.</p> <p>My question has come about when trying to understand why buffer.WriteString() is a faster concatenation method than just MyString + OldString.</p> <p>How does this operation differ once compiled and why is there such a massive GC saving? I can understand the copy, resize and add process but not how buffer.WriteString() circumvents that.</p> <p>Thanks guys.</p> <hr/>**评论:**<br/><br/>itsmontoya: <pre><p>You can continually grow a slice and it will cause fewer allocations than constantly merging strings. </p> <p>You may want to look into how slices allocate when they are appended to.</p> <p>There is a small caveat where some very basic string concatenations will optimize very well and outperform byte appending.</p></pre>fnb_theory: <pre><blockquote> <p>You may want to look into how slices allocate when they are appended to.</p> </blockquote> <p>I&#39;ll definitely check that out. Thanks.</p></pre>itsmontoya: <pre><p>If you have any misc questions, feel free to PM me or hit me up on IRC</p></pre>fnb_theory: <pre><p>Shot, I appreciate it.</p></pre>lstokeworth: <pre><p>The source is easily findable on the interweb. Go to the documentation for <a href="https://godoc.org/bytes#Buffer.WriteString" rel="nofollow">WriteString</a>. Click the function name to view the source.</p> <p>There is no advantage to using bytes.Buffer instead of just MyString + OldString. The following two snippets have approximately the same performance:</p> <pre><code>var buf bytes.Buffer buf.WriteString(MyString) bur.WriteString(OldString) s := buf.String s := MyString + OldString </code></pre> <p>There is an advantage to using bytes.Buffer when you can eliminate intermediate string values. For example:</p> <pre><code>for i := 0; i &lt; 10; i ++ { buf.WriteString(strconv.Itoa(i)) } s := buf.String() </code></pre> <p>is faster than </p> <pre><code>s := &#34;&#34; for i := 0; i &lt; 10; i ++ { s += strconv.Itoa(i) } </code></pre></pre>fnb_theory: <pre><p>I&#39;ll read about it, thanks. I&#39;ve got a service running at the moment which deals with a lot of string concatenations and have seen a massive advantage using WriteString() rather than String + String.</p> <p>As far as I understand it, both need to perform the resize, copy. The specific metric that went down was &#34;runtime.memmove&#34;.</p> <p>Perhaps I am reading into this incorrectly?</p> <p>I&#39;m running go v1.7.3</p></pre>sevs44936: <pre><p>AFAIK the improvement comes from this:</p> <pre><code>s := &#34;&#34; for i := 0; i &lt; 10; i ++ { s += strconv.Itoa(i) } </code></pre> <p>First step, &#34;0&#34; is appended to &#34;&#34;. A new string with length 1 is allocated, &#34;0&#34; is copied into that one, the string &#34;&#34; is gc&#39;d.<br/> Second step, &#34;1&#34; is appended to &#34;0&#34;. Length 2 string is allocated, &#34;0&#34; is copied, &#34;1&#34; is copied, &#34;0&#34; is gc&#39;d.<br/> Third step, &#34;2&#34; is appended to &#34;01&#34;, Length 3 string allocated, &#34;01&#34; copied, &#34;2&#34; copied, &#34;01&#34; gc&#39;d.<br/> ... etc. </p> <pre><code>for i := 0; i &lt; 10; i ++ { buf.WriteString(strconv.Itoa(i)) } s := buf.String() </code></pre> <p>bytes.Buffer starts with a capacity of 64 Bytes, i.e. &#34;0&#34; is copied to the buffer, &#34;1&#34; is copied, &#34;2&#34; is copied, etc, etc.<br/> When those 64 bytes are full a new slice with 2 * 64 Bytes is allocated (+ the length of the write that triggered the expansion). Everything is copied to the new buffer. After that 2 * 128, 2 * 256, etc.</p> <p>Each string append causes one allocation and creates two strings for the GC.</p> <p>b.WriteString causes exponentially less and less allocations the more you write to one buffer (which could be further optimized if you have an idea about the final size, use b.Grow to set the buffer capacity in that area - might save you a couple alloc&#39;s)</p> <p>Hope this makes sense ;)</p></pre>fnb_theory: <pre><p>Ahhhh! I see. That makes sense. Do i just declare b.Grow once for that buffer?</p></pre>sevs44936: <pre><p>b.Grow(n) just assures you that another n bytes can fit in the buffer without alloc/copy and thus can be called multiple times. Always depends on your use case.</p> <p>For example if you know you will concat at least 100 strings and those have an average length of 50 bytes, call Grow() at the start with 5000. If the buffer is then filled it gets expanded to a capacity of 10000. Otherwise you&#39;d end up with going though multiple steps - ie. 64, 128, 256, ... - which are unnecessary if you already know you need more space.</p></pre>fnb_theory: <pre><p>That&#39;s pretty cool. I&#39;m going to keep that in mind when I do another code optimisation run.</p></pre>hobbified: <pre><p>Buffer is mutable, unlike a string. When you concatenate two strings, you necessarily get a third string that&#39;s different from either. When you append to a buffer, you change the existing buffer. That&#39;s at least half the difference right there.</p></pre>fnb_theory: <pre><p>I can see that having quite a performance saving if you&#39;re dealing with large amounts of string concats. (Which we do)</p></pre>tmornini: <pre><blockquote> <p>My question has come about when trying to understand why buffer.WriteString() is a faster concatenation method than just MyString + OldString</p> </blockquote> <p>Actually, this seems to be an optimization that <em>used</em> to be true, but no longer is.</p> <p>EDIT: When asked I was unable to find a post I read recently. Many apologies for stating something I could not back up.</p></pre>lilgnomeo: <pre><p>Can you find a source that supports this?</p></pre>tmornini: <pre><p>I just looked for a fairly recent article I read that indicated that in many cases, string concatenation isn&#39;t as slow as it used to be.</p> <p>Unfortunately I cannot find it, so I retract my statement, and apologize for stating something I cannot back up.</p></pre>fnb_theory: <pre><p>I&#39;ve still seen major improvements in go v1.7.3</p></pre>tmornini: <pre><p>OK, thanks for that.</p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

493 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传