Why is it faster to manipulate elements of a pointer to a slice?

polaris · · 440 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>In this test program, I have 2 functions that manipulate elements of a slice. One function just gets passed the slice, the other gets passed the address of the slice. On my machine (x86-64 intel i7 6700k) it is consistently faster to manipulate elements of a pointer to a slice (you can switch up the order of chg() and chgPtr() to see that the relationship still holds). This is the opposite of what I expected. Why is this so?</p> <p>Edit: On my machine it is anywhere from 5% to 7% faster to manipulate elements of a pointer to a slice</p> <pre><code>package main import ( &#34;fmt&#34; &#34;math/rand&#34; &#34;time&#34; ) type Stuff struct { A float64 B float64 } func main() { rand.Seed(time.Now().UnixNano()) var lst []Stuff // init list for i := 0; i &lt; 1000000; i++ { lst = append(lst, Stuff{float64(i), float64(i)}) } // make sure first to be measured isn&#39;t at disadvantage chg(lst) avgTbMinusTa := int64(0) avgPercent := float64(0) iterations := 100 for i := 0; i &lt; iterations; i++ { taStart := time.Now().UnixNano() chg(lst) taStop := time.Now().UnixNano() tbStart := time.Now().UnixNano() chgPtr(&amp;lst) tbStop := time.Now().UnixNano() ta := taStop - taStart tb := tbStop - tbStart avgTbMinusTa += (tb - ta) avgPercent += ((float64(tb) / float64(ta)) * 100) } fmt.Println(&#34;avg Tb - Ta =&#34;, float64(avgTbMinusTa)/(float64(iterations)*1000000), &#34;ms&#34;) fmt.Println(&#34;Tb % of Ta =&#34;, avgPercent/float64(iterations)) } func chg(s []Stuff) { for i := 0; i &lt; len(s); i++ { s[i].A = rand.Float64() } } func chgPtr(s *[]Stuff) { for i := 0; i &lt; len(*s); i++ { (*s)[i].B = rand.Float64() } } </code></pre> <hr/>**评论:**<br/><br/>anossov: <pre><p>I don&#39;t know, however, I will show you the proper way of benchmarking go stuff:</p> <h1>File slice_test.go</h1> <pre><code>package main import ( &#34;math/rand&#34; &#34;testing&#34; &#34;time&#34; ) type Stuff struct { A float64 B float64 } func setup() (lst []Stuff) { rand.Seed(time.Now().UnixNano()) for i := 0; i &lt; 1000000; i++ { lst = append(lst, Stuff{float64(i), float64(i)}) } return lst } func BenchmarkCopy(b *testing.B) { lst := setup() b.ResetTimer() for i := 0; i &lt; b.N; i++ { chg(lst) } } func BenchmarkPtr(b *testing.B) { lst := setup() b.ResetTimer() for i := 0; i &lt; b.N; i++ { chgPtr(&amp;lst) } } func chg(s []Stuff) { for i := 0; i &lt; len(s); i++ { s[i].A = rand.Float64() } } func chgPtr(s *[]Stuff) { for i := 0; i &lt; len(*s); i++ { (*s)[i].B = rand.Float64() } } </code></pre> <h1>Benchmarking</h1> <pre><code>$ go test -bench=. -benchtime=10s testing: warning: no tests to run PASS BenchmarkCopy 1000 25255135 ns/op BenchmarkPtr 1000 25572549 ns/op ok _/home/anossov/slice 56.059s </code></pre> <p> </p> <p>It&#39;s not faster for me ¯\_(ツ)_/¯ (i5 6600k @3.5GHz, Ubuntu subsystem on Windows 10)</p> <p><a href="https://golang.org/pkg/testing/#hdr-Benchmarks">https://golang.org/pkg/testing/#hdr-Benchmarks</a></p></pre>epiphanius_zeno: <pre><p>Thanks! I&#39;m not familiar with the testing package.</p></pre>Morgahl: <pre><p>Using <code>rand.Float64()</code> in this case hides the actual array iteration costs in you are attempting to show in your benchmark. Simply changing this to a simple static value shows that passing just the slice header is indeed slightly faster (in my case only ~1.5%).</p></pre>drunken_thor: <pre><p>Why is that the opposite of what you expect? If you pass a pointer, all you are passing is an address. If you pass a slice you are passing the full value which actually means that the value is being copied into a new slice to assign to your parameter value. </p></pre>jmoiron: <pre><p>Only the slice header is copied.</p></pre>epiphanius_zeno: <pre><p>I expected passing a pointer to a slice to be the same speed or slower. In c/c++ if you pass in a vector it makes a full copy of the vector, but I don&#39;t think that&#39;s what happens with slices. You can manipulate elements of a slice and those changes will remain outside of the function. How could changes remain if it copied the slice?</p> <p>My reasoning was that by passing a pointer to a slice you&#39;re actually passing a pointer to a pointer to a slice, and every time you update an element you&#39;d have to de-reference twice. </p></pre>kaliku: <pre><p>A slice contains 3 things:<br/> * pointer to the underlying array<br/> * len<br/> * cap </p> <p>Go, always passing by value, will copy this data structure and allocate on the stack for all three items, which is a greater allocation than just allocating for the pointer. </p> <p>The changes remain because you are manipulating the underlying array. </p></pre>

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

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