<p>I created a library for Right/Left string padding which has variable allocation <code>caching</code> for common use cases:
<a href="https://github.com/Rhymond/gopad/blob/master/gopad.go#L8" rel="nofollow">https://github.com/Rhymond/gopad/blob/master/gopad.go#L8</a></p>
<p>Is it good practice to do that?
I'm looking for an advice, thanks in advance!</p>
<hr/>**评论:**<br/><br/>metamatic: <pre><p>Given <a href="http://herman.asia/efficient-string-concatenation-in-go" rel="nofollow">how cheap</a> appending one byte to a <a href="https://golang.org/pkg/bytes/#Buffer" rel="nofollow">Buffer</a> is, I'm very skeptical that you'll get any speedup by having pre-build 2, 3, 4 and 5 byte sequences.</p>
<p>In fact, given modern CPU architectures, I suspect that appending one byte to a buffer 5 times is going to be faster than going out to main memory to fetch a sequence of 5 bytes you created at compile time.</p></pre>tscs37: <pre><p>appending is only cheap when the capacity is sufficient.</p>
<p>From experience, pre-allocating a buffer in memory instead of allocating til max capacity is a factor of 1.5 to 2 times in performance until the cache is fully warmed.</p>
<p>The reason is that if the capacity is not sufficient, you will most likely have to hit the runtime or even worse, malloc.</p>
<p>In a tight loop, going into runtime or doing a syscall can shit all over the cache and worsen performance.</p></pre>raima220: <pre><p>I though the same before, but I made benchmarks:</p>
<p>With "Cache":
BenchmarkPad-8 300000 3673 ns/op</p>
<p>Without:
BenchmarkPad-8 300000 4579 ns/op</p>
<p>You can find my benchmark test here:</p>
<p><a href="https://github.com/Rhymond/gopad/blob/master/gopad_test.go" rel="nofollow">https://github.com/Rhymond/gopad/blob/master/gopad_test.go</a></p></pre>metamatic: <pre><p>Interesting. I'm surprised that (a) it's faster and (b) by that much.</p>
<p>Would be interesting to look at the generated code, if I knew X86 assembler...</p>
<p>I did some more tests, and it's even faster if you just keep the buffer around and reset it, rather than creating a new buffer every time. I'm also inclined to optimize for the common case of only wanting to pad with a single rune; I don't think I've ever wanted to pad with more than one.</p>
<pre><code>var buff bytes.Buffer
func spad(s string, l int, ch rune, isl bool) string {
buff.Reset()
sl := utf8.RuneCountInString(s)
n := l - sl
if n <= 1 || l < 1 {
return s
}
if !isl {
buff.WriteString(s)
}
for ; n > 0; n-- {
buff.WriteRune(ch)
}
if isl {
buff.WriteString(s)
}
return buff.String()
}
</code></pre></pre>xienze: <pre><blockquote>
<p>and it's even faster if you just keep the buffer around and reset it</p>
</blockquote>
<p>But then you're not thread-safe unless you control access to the buffer, which will slow you right back down. Or you can make the caller provide the buffer, but that's an inconvenience and a bit of a leaky abstraction. Tradeoffs.</p></pre>metamatic: <pre><p>Indeed. And the whole thing's an exercise in premature optimization to start with.</p></pre>xienze: <pre><p>Well it depends. If this string padding function is going to get called all the time then OP's idea is a good move. If it's just used occasionally and not in a hot path? Who cares.</p></pre>hell_0n_wheel: <pre><p>What problem did you solve by writing that code?</p>
<p>Did you profile the code before and after adding the solution, to confirm it did what you expected?</p></pre>raima220: <pre><p>Yes, and it looks like that <code>cached</code> is faster</p>
<p>With "Cache":
BenchmarkPad-8 300000 3673 ns/op</p>
<p>Without:
BenchmarkPad-8 300000 4579 ns/op</p>
<p>You can find my benchmark test here:</p>
<p><a href="https://github.com/Rhymond/gopad/blob/master/gopad_test.go" rel="nofollow">https://github.com/Rhymond/gopad/blob/master/gopad_test.go</a></p></pre>hell_0n_wheel: <pre><p>Well then you answered your own question.</p>
<p>As you stated it here, there's no context for your question, so we can't understand it, let alone give you a good answer.</p>
<p>Consider this next time you post: <a href="https://cs.stackexchange.com/help/how-to-ask" rel="nofollow">https://cs.stackexchange.com/help/how-to-ask</a></p></pre>Snabel3: <pre><p>The cache is not optimal as a array since the code will have to look up the pointer to the value every time. I don't think you are earning any performance with all these lookups. </p>
<p>The cache should be a constant (if used at all). </p>
<p>Try using string.Repeat() with a single space instead of a for-loop and use a slice to get the length you want. Should be cleaner and safer code. </p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传