Live data from Hacker News

Go Optimization Guide

goperf.dev

41–50 of 173 posts

Re: Go Optimization Guide

#41

Curious to know what people are building where you need to optimise like this? eg Struct Field Alignment https://goperf.dev/01-common-patterns/fields-alignment/#avoi...

False sharing is an absolutely classic Concurrency 101 lesson, nothing remarkable about it.

Re: Go Optimization Guide

#42
post #28
post #19

Earlier quoted context omitted.

No. If you have a moving multi generational GC, allocation is literally just an increment for short lived objects.

This is about go not Java. Go makes different tradeoffs and does not have moving multigenerational GC.

[deleted]

Re: Go Optimization Guide

#44
post #31
post #19

Earlier quoted context omitted.

No. If you have a moving multi generational GC, allocation is literally just an increment for short lived objects.

If you have a moving, generational GC, then all the benefits of fast allocation are lost due to data moving and costly memory barriers.

[deleted]

Re: Go Optimization Guide

#45
Checking out the first example—object pools—I was initially blown away that this is not only possible but it produces no warnings of any kind:

    pool := sync.Pool{
        New: func() any { return 42 }
    }

    a := pool.Get()

    pool.Put("hello")
    pool.Put(struct{}{})

    b := pool.Get()
    c := pool.Get()
    d := pool.Get()

    fmt.Println(a, b, c, d)
Of course, the answer is that this API existed before generics so it just takes and returns `any` (née `interface{}`). It just feels as though golang might be strongly typed in principle, but in practice there are APIs left and rigth that escape out of the type system and lose all of the actual benefits of having it in the first place.

Is a type system all that helpful if you have to keep turning it off any time you want to do something even slightly interesting?

Also I can't help but notice that there's no API to reset values to some initialized default. Shouldn't there be some sort of (perhaps optional) `Clear` callback that resets values back to a sane default, rather than forcing every caller to remember to do so themselves?

Re: Go Optimization Guide

#46
post #14

Earlier quoted context omitted.

I guess if you allocate more than you need upfront that it could increase memory usage.

I don't get it. The pool uses weak pointers under the hood right? If you allocate too much up front, the stuff you don't need will get garbage collected. It's no worse than doing the same without a pool, right?

What the top commenter probably failed to mention, and jensneuse tried to explain is that sync.Pool makes an assumption that the size cost of pooled items are similar. If you are pooling buffers (eg: []byte) or any other type with backing memory which during use can/will grow beyond their initial capacity, can lead to a scenario where backing arrays which have grown to MB capacities are returned by the pool to be used for a few KB, and the KB buffers are returned to high memory jobs which in turn grow the backing arrays to MB and return to the pool.

If that's the case, it's usually better to have non-global pools, pool ranges, drop things after a certain capacity, etc.:

https://github.com/golang/go/issues/23199 https://github.com/golang/go/blob/7e394a2/src/net/http/h2_bu...

Re: Go Optimization Guide

#47

You can often fool yourself by using sync.Pool. pprof looks great because no allocs in benchmarks but memory usage goes through the roof. It's important to measure real world benefits, if any, and not just synthetic benchmarks.

also no one GCs sync.Pool. After a spike in utilization, live with increased memory usage until program restart.

Re: Go Optimization Guide

#48

Every perf guide recommends to minimize allocations to reduce GC times, but if you look at pprof of a Go app, GC mark phase is what takes time, not GC sweep. GC mark always starts with known live roots (goroutine stacks, globals, etc) and traverse references from there colouring every pointer. To minimize GC time it is best to avoid _long living_ allocations. Short lived allocations, those which GC mark phase will ne…

Are you including in this analysis the amount of time/resources it takes to allocate? GC isn't the only thing you want to minimize for when you're making a high performance system.

From that perspective it boils down to "do less", which is what any perf guide already includes, allocations is just no different from anything else what app do.

My comment is more about "reduce allocations to reduce GC pressure" advice seen everywhere. It doesn't tell the whole story. Short lived allocation doesn't introduce any GC pressure: you'll be hard pressed to see GC sweep phase on pprof without zooming. People take this advice, spend time and energy hunting down allocations, just to see that total GC time remained the same after all that effort, because they were focusing on wrong type of allocations.

Re: Go Optimization Guide

#49

Curious to know what people are building where you need to optimise like this? eg Struct Field Alignment https://goperf.dev/01-common-patterns/fields-alignment/#avoi...

Something that shouldn’t be written in a GC language.

Re: Go Optimization Guide

#50
post #45

Checking out the first example—object pools—I was initially blown away that this is not only possible but it produces no warnings of any kind: pool := sync.Pool{ New: func() any { return 42 } } a := pool.Get() pool.Put("hello") pool.Put(struct{}{}) b := pool.Get() c := pool.Get() d := pool.Get() fmt.Println(a, b, c, d) Of course, the answer is that this API existed before generics so it just takes and returns `any` (…

You never programmed in Go, I assume? Then you have to understand that the type of `pool.Get()` is `any`, the wildcard type in Go. It is a type, and if you want the underlying value, you have to get it out by asserting the correct type. This cannot be solved with generics. There's no way in Java, Rust or C++ to express this either, unless it is a pool for a single type, in which case Go generics indeed could handle that as well. But since Go is backwards compatible, this particular construct has to stay.

> Also I can't help but notice that there's no API to reset values to some initialized default.

That's what the New function does, isn't it?

BTW, the code you posted isn't syntactically correct. It needs a comma on the second line.

Post reply on HN