return fmt.sprintf vs string

agolangf · · 1822 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I&#39;ve seen this in a few peoples source and I was wondering why.</p> <p>Things like:</p> <pre><code>func (c *config) HostString() string { return fmt.Sprintf(&#34;%s:%d&#34;, c.WebHost, c.WebPort) } </code></pre> <p>why not return a string is there a performance gain from this of some sort? If I look elsewhere where that method is called it&#39;s called like:</p> <pre><code>fmt.printf(config.HostString()) </code></pre> <p>so I&#39;m not understanding why do that as opposed to returning a string instead. </p> <hr/>**评论:**<br/><br/>anonfunction: <pre><p>fmt.Sprintf() <em>does</em> return a string</p></pre>Darxide-: <pre><p>yes, but why not just return a string in the function instead of go through another method to return a string. I could just as easily do.</p> <pre><code>someString := c.WebHost + &#34;:&#34; + c.WebPort //This might be wrong concat syntax in Go I haven&#39;t done any real Go programming in like 6 months </code></pre></pre>anonfunction: <pre><p>Because that won&#39;t work, WebHost is a string where WebPort is an int, and in Golang you can&#39;t concat two different types. fmt.Sprintf() is a helper that handles the typesetting for you, notice the %s and %d used. Check out <a href="https://golang.org/pkg/fmt/" rel="nofollow">https://golang.org/pkg/fmt/</a> for all the details. </p></pre>Femaref: <pre><p>Well you can do <code>strconv.Itoa(c.WebPort)</code>, but then you have overhead anyway.</p></pre>anonfunction: <pre><p>Sure you can, that&#39;s exactly what I did in a quick benchmark I put together: <a href="https://gist.github.com/montanaflynn/58df9a48e40baca8dcdc" rel="nofollow">https://gist.github.com/montanaflynn/58df9a48e40baca8dcdc</a></p></pre>calebdoxsey: <pre><p><code>WebPort</code> is an integer. Go is strongly typed so you can&#39;t concatenate an integer to a string.</p></pre>Fwippy: <pre><p>Well, <code>c.WebPort</code> isn&#39;t a string, so your code won&#39;t work.</p> <p><code>printf</code> functions are pretty readable to many programmers. Seeing <code>%s:%d</code> immediately shows what the output string is going to look like. I personally prefer seeing the format up front, and the data later.</p></pre>TheMerovius: <pre><p>It&#39;s trivial to check, that fmt.Sprintf is around 10x slower (at least on my machine) then concatenating strings. I would consider it more readable, though. And that gets compounded by consistency: A lot of times you don&#39;t only want to concatenate strings, but you also want to do some kind of conversion (so e.g <code>fmt.Sprintf(&#34;%s:%d&#34;, addr.Hostname, addr.Port)</code> or something). This is pretty unreadable, so for consistency you just use <code>Sprintf</code> all the time.</p> <p>It is also considered bad practice to do such kind of premature optimizations. Chances are, that the string conversion is not in the hotpath of your program and if it is, benchmarking will tell you and you can still change it later. But it pretty much is guaranteed not to be. Trust me.</p></pre>Darxide-: <pre><p>Aye I agree with pre optimisations, but coming from other languages I would just build out strings so I found the fmt.Sprintf quite strange and was trying to find the reasoning behind it. Thanks for the good answer. </p></pre>anonfunction: <pre><p>As <a href="/u/TheMerovius" rel="nofollow">/u/TheMerovius</a> mentioned it is easy to benchmark in Golang thanks to the built in testing and benchmarking tools. Since he didn&#39;t include any example or code to do so I whipped it up and added it as a gist to GitHub: <a href="https://gist.github.com/montanaflynn/58df9a48e40baca8dcdc" rel="nofollow">https://gist.github.com/montanaflynn/58df9a48e40baca8dcdc</a></p> <p>You can run the code yourself with the following commands:</p> <pre><code># Get the file into current working directory (requires git) git clone https://gist.github.com/58df9a48e40baca8dcdc.git; cd 58df9a48e40baca8dcdc # Run go test benchmarks (the . is regex for all benchmarks) go test -bench=. </code></pre> <p>Here are the results from running the benchmarks on my old single core laptop:</p> <pre><code>BenchmarkSprintf 500000 2952 ns/op BenchmarkStrconv 2000000 991 ns/op </code></pre> <p>As you can see Sprintf is about 4 times slower then Strconv but much more readable and at these speeds unlikely to be a bottleneck in any real world applications.</p> <p>To learn more about the built in benchmarking in Go: <a href="https://golang.org/pkg/testing/#hdr-Benchmarks" rel="nofollow">https://golang.org/pkg/testing/#hdr-Benchmarks</a></p></pre>elithrar_: <pre><p>Speaking of premature optimization, this post on SO shows simple string concat vs. appending to a <code>bytes.Buffer</code> vs. using <code>copy</code> - <a href="http://stackoverflow.com/a/23857998" rel="nofollow">http://stackoverflow.com/a/23857998</a></p></pre>anonfunction: <pre><p>I don&#39;t think those tests are testing the same things or showing the correct results.</p> <p>For example the <code>copy</code> example is dealing with <code>[]byte</code> and not a <code>string</code> like the others. Secondly using b.N as part of the test itself will mean that depending on the speed it could be dramatically different then the other benchmarks. This is due to Golang benchmarks trying to fit as many tests into a default of 1 second as possible. Finally the use of b.ResetTimer() and b.StopTimer() around only for loop doesn&#39;t make sense in this case. It&#39;s due to these mistakes that I think he came to the conclusion of <code>copy</code> being ~12,000x faster.</p> <p>Here&#39;s my version[1] of the same test, without <code>copy</code> as I couldn&#39;t wrap my head around what it&#39;s actually doing enough to create a 1:1 benchmark using strings.</p> <p>[1] <a href="https://gist.github.com/montanaflynn/180b897bb2945ec36b9e" rel="nofollow">https://gist.github.com/montanaflynn/180b897bb2945ec36b9e</a></p></pre>TheMerovius: <pre><p>Yeah, my code was a bit simpler and I only tested strings (no conversion from int), I guess that&#39;s why the difference was higher for me :)</p></pre>calebdoxsey: <pre><p>Seems fairly common. </p> <p>PHP has <code>sprintf</code>, python has <code>format</code> (<code>&#39;{0:s}:{1:d}&#39;.format(addr.Hostname, addr.Port)</code>), C# has <code>String.Format</code>, Java <code>string.Format</code>, etc... Even Haskell has an implementation of printf. </p></pre>peterbourgon: <pre><p>You&#39;re asking why do</p> <p><code> return fmt.Sprintf(&#34;%s:%d&#34;, c.WebHost, c.WebPort) </code></p> <p>instead of</p> <p><code> return c.WebHost + &#34;:&#34; + strconv.Itoa(c.WebPort) </code></p> <p>? It&#39;s just nicer to read, and easier to modify in the future.</p></pre>chzyer: <pre><p>Even <code>fmt.Sprintf</code> is 10times slower, but it still much faster than IO which will be the bottleneck and maybe not a high frequency path. So, <code>fmt.Sprintf</code> is just OK. However, it maybe is better that merge <code>c.WebHost</code>, <code>c.WebPort</code> into one field named <code>c.Web</code>?</p></pre>feelobot: <pre><p>I was under the impression you could just do </p> <pre><code>fmt.Sprintf(c.WebHost, &#34;:&#34;, c.WebPort) </code></pre></pre>

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

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