DEV Community

Vee Satayamas
Vee Satayamas

Posted on

My frequent mistake in Go

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)
}
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

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)         
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
ccoveille profile image
Christophe Colombier

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.

Collapse
 
veer66 profile image
Vee Satayamas • Edited

it would be a stupid idea, as you named your method ShowWidth, but I'm using as an example

Don't worry. My entire example is unrealistic since the first place.

Except that, and if you are not looking for an optimization challenge, you can count on the compiler to optimize things.

You're right. I should stop thinking like coding for MOS 6502.