Live data from Hacker News

Go Optimization Guide

goperf.dev

51–60 of 173 posts

Re: Go Optimization Guide

#51
post #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 t…

I assume they're referring to the fact that a Pool can hold different types instead of being a collection of items of only one homogeneous type.

Re: Go Optimization Guide

#52
post #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 t…

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

But that's only run when the pool needs to allocate more space. What GP seems to expect is that sync.Pool() would always return a zeroed structure, just as Golang allocation does.

I think Golang's implementation does make sense, as sync.Pool() is clearly an optimization you use when performance is an issue; and in that case you almost certainly want to only initialize parts of the struct that are needed. But I can see why it would be surprising.

> [any] is a type

It's typed the way Python is typed, not the way Rust or C are typed; so loses the "if it compiles there's a good chance it's correct" property that people want from statically typed languages.

I don't use sync.Pool, but it does seem like now that we have generics, having a typed pool would be better.

Re: Go Optimization Guide

#53
post #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 t…

Is there a time in your career where an object pool absolutely had to contain an unbounded set of types? Any time when you would try know at compile time the total set of types a pool should contain?

Re: Go Optimization Guide

#54
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` (…

While I think you're right (generics might be useful there), it's fairly easy to wrap the `sync` primitives such as `sync.Pool` and `sync.Map` into your specific use case.

Go is pretty strict about breaking changes, so they probably won't change the current implementations; maybe we'll see a v2 version, or maybe not. The more code you have, the more code you have to maintain, and given Go's backward-compatibility promises, that's a lot of work.

Re: Go Optimization Guide

#55
post #52
post #50

Earlier quoted context omitted.

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 t…

> That's what the New function does, isn't it? But that's only run when the pool needs to allocate more space. What GP seems to expect is that sync.Pool() would always return a zeroed structure, just as Golang allocation does. I think Golang's implementation does make sense, as sync.Pool() is clearly an optimization you use when performance is an issue; and in that case you almost certainly want to only initialize pa…

> so loses the "if it compiles there's a good chance it's correct" property that people want from statically typed languages.

If that's what people actually wanted, Coq and friends would be household names, not the obscure oddities that they are. All the languages that people actually use on any kind of regular basis require you to write tests in order to gain that sense of correctness, which also ends up validating type-correctness as a natural byproduct.

"A machine can help me refactor my code" is the property that most attracts people to the statically typed languages that are normally used. With "I can write posts about it on the internet" being the secondary property of interest.

Re: Go Optimization Guide

#56
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` (…

A lot of languages have top types

Re: Go Optimization Guide

#57
post #7

You're not really writing 'Go' anymore when you're optimising it, it's defeating the point of the language as a simple but powerful interface over networked services.

I think at least some of the patterns shared in the document, using zero-copy, ordering struct properties are all very idiomatic. Writing code in this manner is writing good Go code.

Re: Go Optimization Guide

#58
post #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.

I don't think GC has anything to do here, doing manual memory allocation we might hit the same problem.

Re: Go Optimization Guide

#59
post #55
post #52

Earlier quoted context omitted.

> That's what the New function does, isn't it? But that's only run when the pool needs to allocate more space. What GP seems to expect is that sync.Pool() would always return a zeroed structure, just as Golang allocation does. I think Golang's implementation does make sense, as sync.Pool() is clearly an optimization you use when performance is an issue; and in that case you almost certainly want to only initialize pa…

> so loses the "if it compiles there's a good chance it's correct" property that people want from statically typed languages. If that's what people actually wanted, Coq and friends would be household names, not the obscure oddities that they are. All the languages that people actually use on any kind of regular basis require you to write tests in order to gain that sense of correctness, which also ends up validating…

It's a spectrum, with costs and benefits at each level. I lock my front door even though I don't have bars on my windows; I prefer Golang, where doing a basic compile will catch a fair number of errors and testing will catch the rest, to Python or Perl where testing is the only way to catch errors.

Re: Go Optimization Guide

#60
post #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.

GC is not relevant in this case, it's about whether you can make structs fit in cache lines and CPU registers. Mechanical sympathy is the googleable phrase. GC is a few layers further away.
Post reply on HN