My frequent mistake in Go is overusing pointers, like this unrealistic example below:
type BBox struct {
X1 float64
Y1 float64
X2 float64
Y2 float64
}
func ShowWidth(b *BBox) {
w := math.Abs(b.X2 - b.X1)
fmt.Println(w)
}
func main() {
b1 := BBox{X1: 10.1, Y1: 100.2, X2: 1024.4, Y2: 4096.188888}
b2 := BBox{X1: 10.1, Y1: 100.2, X2: 2024.4, Y2: 4096.188888}
b3 := BBox{X1: 10.1, Y1: 100.2, X2: 3024.4, Y2: 4096.188888}
ShowWidth(&b1)
ShowWidth(&b2)
ShowWidth(&b3)
}
I pass a pointer of BBox to ShowWidth, which according to @meeusdylan's post, it slows down my program because the garbage collector has to determine if a BBox must be in stack or heap.
In the alternative code below, I don't use pointer.
func ShowWidth(b BBox) {
w := math.Abs(b.X2 - b.X1)
fmt.Println(w)
}
func main() {
b1 := BBox{X1: 10.1, Y1: 100.2, X2: 1024.4, Y2: 4096.188888}
b2 := BBox{X1: 10.1, Y1: 100.2, X2: 2024.4, Y2: 4096.188888}
b3 := BBox{X1: 10.1, Y1: 100.2, X2: 3024.4, Y2: 4096.188888}
ShowWidth(b1)
ShowWidth(b2)
ShowWidth(b3)
}
I worried that my program will copy the entire BBox every time ShowWidth is called. So, I checked the generated asssembly code. It looks like this:
ShowWidth(b1)
0x48098e f20f10059ab60300 MOVSD_XMM $f64.4024333333333333(SB), X0
0x480996 f20f100d9ab60300 MOVSD_XMM $f64.40590ccccccccccd(SB), X1
0x48099e f20f10159ab60300 MOVSD_XMM $f64.409001999999999a(SB), X2
0x4809a6 f20f101daab60300 MOVSD_XMM $f64.40b000305af6c69b(SB), X3
0x4809ae e82dffffff CALL main.ShowWidth(SB)
So, what I worried was true. MOVSD_XMM is for copying value from a member of a BBox in memory to a register one-by-one. You may see MOVSD_XMM was called 4 times per each ShowWidth call.
I didn't measure which one is faster or slower. I've heard that Skymont support loads per cycle. And, I wish they meant loading float64 using MOVSD_XMM as well. So, copying entire BBox is hopefully fast. And, at least, as far as I have been told, a BBox will definitely remain in stack without a need of checking by the GC.
Moreover, passing by value seems to comply to Go community better than pointer. So it will look familiar, and everyone will be happy to see passing by value.
My plan is avoiding pointer by default, and I will use it only when I have to. About performance, I think I may have to benchmark before using a pointer. Or if the speed is acceptable, I won't optimize.
Top comments (2)
For me pointer only makes sense when you need to modify the variable you pass by reference.
Like if you were passing a BBox, and setting a variable named Width into b (it would be a stupid idea, as you named your method ShowWidth, but I'm using as an example)
Except that, and if you are not looking for an optimization challenge, you can count on the compiler to optimize things.
Don't worry. My entire example is unrealistic since the first place.
You're right. I should stop thinking like coding for MOS 6502.