<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 (
"fmt"
"math/rand"
"time"
)
type Stuff struct {
A float64
B float64
}
func main() {
rand.Seed(time.Now().UnixNano())
var lst []Stuff
// init list
for i := 0; i < 1000000; i++ {
lst = append(lst, Stuff{float64(i), float64(i)})
}
// make sure first to be measured isn't at disadvantage
chg(lst)
avgTbMinusTa := int64(0)
avgPercent := float64(0)
iterations := 100
for i := 0; i < iterations; i++ {
taStart := time.Now().UnixNano()
chg(lst)
taStop := time.Now().UnixNano()
tbStart := time.Now().UnixNano()
chgPtr(&lst)
tbStop := time.Now().UnixNano()
ta := taStop - taStart
tb := tbStop - tbStart
avgTbMinusTa += (tb - ta)
avgPercent += ((float64(tb) / float64(ta)) * 100)
}
fmt.Println("avg Tb - Ta =", float64(avgTbMinusTa)/(float64(iterations)*1000000), "ms")
fmt.Println("Tb % of Ta =", avgPercent/float64(iterations))
}
func chg(s []Stuff) {
for i := 0; i < len(s); i++ {
s[i].A = rand.Float64()
}
}
func chgPtr(s *[]Stuff) {
for i := 0; i < len(*s); i++ {
(*s)[i].B = rand.Float64()
}
}
</code></pre>
<hr/>**评论:**<br/><br/>anossov: <pre><p>I don'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 (
"math/rand"
"testing"
"time"
)
type Stuff struct {
A float64
B float64
}
func setup() (lst []Stuff) {
rand.Seed(time.Now().UnixNano())
for i := 0; i < 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 < b.N; i++ {
chg(lst)
}
}
func BenchmarkPtr(b *testing.B) {
lst := setup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
chgPtr(&lst)
}
}
func chg(s []Stuff) {
for i := 0; i < len(s); i++ {
s[i].A = rand.Float64()
}
}
func chgPtr(s *[]Stuff) {
for i := 0; i < 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'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'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't think that'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're actually passing a pointer to a pointer to a slice, and every time you update an element you'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
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传