Live data from Hacker News

Go Optimization Guide

goperf.dev

61–70 of 173 posts

Re: Go Optimization Guide

#61
post #59
post #55

Earlier quoted context omitted.

> 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.

> where doing a basic compile will catch a fair number of errors

In the case of refactoring this is incredibly useful. It doesn't say much about the correctness of your program, though.

Re: Go Optimization Guide

#62

Huh, this surprises me about Golang, didn't realise it was so similar to C with struct alignment. https://goperf.dev/01-common-patterns/fields-alignment/#why-...

Yup, it's a fairly low-level language intended as a replacement to C/C++ but for modern day systems (networked, concurrent, etc). You don't have manual memory management per se but you still need to decide on heap vs stack and consider the hardware.

Re: Go Optimization Guide

#63
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.

What do you mean? If you don't want that level of control over e.g. memory allocation, registries, cache lines etc, there's higher level languages than Go you can pick from, e.g. Java / C# / JS.

Re: Go Optimization Guide

#64
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…

> What GP seems to expect is that sync.Pool() would always return a zeroed structure

Might be, but that's a design decision that has nothing to do with type or generics, isn't it? You seem to refer to a function to drain the pool, which is not needed, and frankly, rather unusual.

> It's typed the way Python is typed

Not in the slightest.

> "if it compiles there's a good chance it's correct"

If you want to compare it to something, it's more like Rust's unwrap(), which will panic if you apply it to the wrong result.

Re: Go Optimization Guide

#65
"Although the struct Data contains a [1024]int array, which is 4 KB (assuming int is 4 bytes on the architecture used)"

Huh,what?

I mean, who uses 32b architecture by default?

Re: Go Optimization Guide

#66
post #64
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…

> What GP seems to expect is that sync.Pool() would always return a zeroed structure Might be, but that's a design decision that has nothing to do with type or generics, isn't it? You seem to refer to a function to drain the pool, which is not needed, and frankly, rather unusual. > It's typed the way Python is typed Not in the slightest. > "if it compiles there's a good chance it's correct" If you want to compare it…

> Not in the slightest.

You know, it's this kind of comment on USENET forums which prompted the creation of StackOverflow. It's not curious and adds nothing to the discussion.

I like Go and use it extensively; and I like having the option to fall back to the `any` type. But it's simply a fact that using the `any` type means that certain properties of the program can't be checked at compile time, in the same way that Python isn't able to check certain properties of the program at compile time.

> If you want to compare it to something, it's more like Rust's unwrap(), which will panic if you apply it to the wrong result.

Rust's unwrap() is used when a type can have one of exactly two underlying types (which is why no type is specified). In this case, getting back an `any` type means the underlying type could literally be anything -- as demonstrated by the example, where they put an integer, a string, and an empty struct into the pool. That's almost certainly not what you wanted, but the compiler won't prevent you from doing it.

Re: Go Optimization Guide

#67
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…

> What GP seems to expect is that sync.Pool() would always return a zeroed structure, just as Golang allocation does.

One could define a new "Pool[T]" type (extending sync.Pool) to get these guarantees:

  type Pool[T any] sync.Pool  // typed def

  func (p *Pool[T]) Get() T { // typed Get
      pp := (*sync.Pool)(p)
      return pp.Get().(T)
  }

  func (p *Pool[T]) Put(v T) { // typed Put
      pp := (*sync.Pool)(p)
      pp.Put(v)
  }

  intpool := Pool[int]{        // alias New
      New: func() any { var zz int; return zz },
  }

  boolpool := Pool[bool]{      // alias New
      New: func() any { var zz bool; return zz },
  }
https://go.dev/play/p/-WG7E-CVXHR

Re: Go Optimization Guide

#68
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…

How is it different than pre-generic Java?

Map/List etc are erased to basically an array of Objects (or a more specific supertype) at compile-time, but you can still use the non-generic version (with a warning) if you want and put any object into a map/list, and get it out as any other type, you having to cast it as the correct type.

Re: Go Optimization Guide

#69
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.

Not at all. Most objects die young and thus are never moved. Also, the time before it is moved is very long compared to CPU operations so it is only statistically relevant (very good throughput, rare, longer tail on latency graphs).

Also, write-only barriers don't have that big of an overhead.

Re: Go Optimization Guide

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

This is still strong typing, even it it's not static typing.

It's static vs. dynamic and strong vs. weak.

https://stackoverflow.com/a/11889763

Post reply on HN