Live data from Hacker News

Go Optimization Guide

goperf.dev

161–170 of 173 posts

Re: Go Optimization Guide

#161
post #159
post #156

Earlier quoted context omitted.

Is that period really that big of a concern when your threads in any language might be context switched away by the OS? It's not a common occurrence on a CPU-timeline at all. Also, it's no accident that every high-performance GC runtime went the moving, generational way.

That time may seem negligible, since the OS can context switch threads anyway, but it’s still additional time during which your code isn’t doing its actual work. Generations are used almost exclusively in moving GCs — precisely to reduce the negative performance impact of data relocation. Non-moving GCs are less invasive, which is why they don’t need generations and can be fully concurrent.

I would rather say that generations are a further improvement upon a moving collector, improving space usage and decreasing the length of the "mark" phase.

And which GC is fully concurrent? I don't think that's possible (though I will preface that I am no expert, only read into the topic on a hobby level) - I believe the most concurrent GC out there is ZGC, which does read barriers and some pointer tricks to make the stop-the-world time independent of the heap size.

Re: Go Optimization Guide

#162
post #89

Earlier quoted context omitted.

It is strong, static, and structural. But structural typing is effectively compile-time duck typing, so it is understandable that some might confuse it with dynamic typing.

Ggp is not talking about structural typing, but about sync.Pool type erasing (it takes `any` values, and returns `any` values). So you can put (and will retrieve) random garbage from it.

> Ggp is not talking about structural typing,

Okay... but the reply was to the parent who questioned if the typing is static. It is by way of structural typing. The compiler enforces that the ducks, so to speak, are compatible. But as an empty interface has no constraints, all types are compatible. Whatever some other comment was talking about is irrelevant.

> but about sync.Pool type erasing

The type isn't erased...?

    p := sync.Pool{New: func() interface{} { return 1 }}
    fmt.Printf("%T", p.Get()) // Prints: int
> So you can put (and will retrieve) random garbage from it.

And you think that makes it dynamically typed? It does not.

Re: Go Optimization Guide

#163
post #161
post #159

Earlier quoted context omitted.

That time may seem negligible, since the OS can context switch threads anyway, but it’s still additional time during which your code isn’t doing its actual work. Generations are used almost exclusively in moving GCs — precisely to reduce the negative performance impact of data relocation. Non-moving GCs are less invasive, which is why they don’t need generations and can be fully concurrent.

I would rather say that generations are a further improvement upon a moving collector, improving space usage and decreasing the length of the "mark" phase. And which GC is fully concurrent? I don't think that's possible (though I will preface that I am no expert, only read into the topic on a hobby level) - I believe the most concurrent GC out there is ZGC, which does read barriers and some pointer tricks to make the…

Java currently has no fully concurrent GC, and due to the volume of garbage it manages and the fact that it moves objects, a truly fully concurrent GC for this language is unlikely to ever exist.

Non-moving GCs, however, can be fully concurrent — as demonstrated by the SGCL project for C++.

In my opinion, the GC for Go is the most likely to become fully concurrent in the future.

Re: Go Optimization Guide

#164
post #163
post #161

Earlier quoted context omitted.

I would rather say that generations are a further improvement upon a moving collector, improving space usage and decreasing the length of the "mark" phase. And which GC is fully concurrent? I don't think that's possible (though I will preface that I am no expert, only read into the topic on a hobby level) - I believe the most concurrent GC out there is ZGC, which does read barriers and some pointer tricks to make the…

Java currently has no fully concurrent GC, and due to the volume of garbage it manages and the fact that it moves objects, a truly fully concurrent GC for this language is unlikely to ever exist. Non-moving GCs, however, can be fully concurrent — as demonstrated by the SGCL project for C++. In my opinion, the GC for Go is the most likely to become fully concurrent in the future.

Is SGCL your project?

In that case, are you doing atomic writes for managed pointers/the read flag on them? I have read a few of your comments on reddit and your flags seem to be per memory page? Still, the synchronization on them may or may not have a more serious performance impact than alternative methods and without a good way to compare it to something like Java which is the state of the art in GC research we can't really comment much on whether it's a net benefit.

Also, have you perhaps tried modeling your design in something like TLA+?

Re: Go Optimization Guide

#165
post #164
post #163

Earlier quoted context omitted.

Java currently has no fully concurrent GC, and due to the volume of garbage it manages and the fact that it moves objects, a truly fully concurrent GC for this language is unlikely to ever exist. Non-moving GCs, however, can be fully concurrent — as demonstrated by the SGCL project for C++. In my opinion, the GC for Go is the most likely to become fully concurrent in the future.

Is SGCL your project? In that case, are you doing atomic writes for managed pointers/the read flag on them? I have read a few of your comments on reddit and your flags seem to be per memory page? Still, the synchronization on them may or may not have a more serious performance impact than alternative methods and without a good way to compare it to something like Java which is the state of the art in GC research we ca…

Yes, SGCL is my project.

You can't write concurrent code without atomic operations — you need them to ensure memory consistency, and concurrent GCs for Java also rely on them. However, atomic loads and stores are cheap, especially on x86. What’s expensive are atomic counters and CAS operations — and SGCL uses those only occasionally.

Java’s GCs do use state-of-the-art technology, but it's technology specifically optimized for moving collectors. SGCL is optimized for non-moving GC, and some operations can be implemented in ways that are simply not applicable to Java’s approach.

I’ve never tried modeling SGCL's algorithms in TLA+.

Re: Go Optimization Guide

#166
post #79

Earlier quoted context omitted.

Sorry, but comparing Python's total absence of typing to extracting a value from any is quite weird. > certain properties of the program can't be checked at compile time Neither can you check if a number is positive or negative, or if a string is empty or not at compile time, but that doesn't make Go similar to COBOL or Forth. `var v any` declares v to be of the type any, not of any arbitrary type, which is what Pyth…

Python doesn't have "total absense of typing". It doesn't have static typing, so compile time checks are not possible (well historically, there's some psuedo static typing things these days). The fact that you can call `+` on some objects but not others is literally the result of the objects being different types. A truly typeless language (or maybe more accurately single type for everything language) is ASM, particu…

The language itself is practically devoid of restrictions on data types. There only are standard lib functions that check the type of their arguments.

Re: Go Optimization Guide

#167
post #123

Earlier quoted context omitted.

Because the order of fields can be significant. It's very relevant for syscalls, and is observable via the reflect package; it'd be strange if the field order was arbitrarily changed (and might change further between releases). I assume the thinking was that this is pretty easy to optimise if you care, and if it's on by default there'd then have to be some opt-out which there isn't a good mechanism for.

> and if it's on by default there'd then have to be some opt-out which there isn't a good mechanism for. Good is subjective, but the mechanism is something already implemented: https://pkg.go.dev/structs#HostLayout

Oh interesting, I've not encountered that before - I suppose because it is currently the default behaviour.

What I was hoping not to find (and fortunately didn't!) was one of Go's magical syntactic comments.

Re: Go Optimization Guide

#169
post #4

Additionally... - https://go101.org/optimizations/101.html - https://github.com/uber-go/guide I wish this content existed as a model context protocol (MCP) tool to connect to my IDE along w/ local LLM. After 6 months or switching between different language projects, it's challenging to remember all the important things.

Embedding those docs in your MCP server takes about 5 seconds with mcp-go's AddResource method https://github.com/mark3labs/mcp-go/blob/main/examples/every...

[deleted]

Re: Go Optimization Guide

#170
post #139
post #134

Earlier quoted context omitted.

> There is nothing in the spec about struct layout De-facto a lot of programs rely on it, so whatever the spec says is irrelevant. Not just for cgo by the way, but also things like binary.Read()/Write().

> but also things like binary.Read()/Write(). binary.Read/Write already uses reflect as far as I can tell, so it wouldn't matter in that case. There is an optimization for numeric values in there, which may be what you are thinking of? But they are specified in the spec and that's not what we're talking about anyway (and if an optimization really had to go, for whatever reason, it wouldn't be the end of the world). D…

I had to think about this one a bit.

Basically, binary.Read/binary.Write are capable of reading/writing struct values. The worry would be that, if the Go compiler reordered fields under the hood, the order may differ between writing the data and reading it back (especially across different versions of Go).

However, because these functions use reflection, they likely wouldn't be affected. While the in-memory layout of the struct fields might get reordered, presumably the reflection order would match the declaration order. Indeed, there is already an Offset field on the reflect.StructField type, and there is no statement anywhere I can find that such offsets must increase monotonically.

So, the fields would remain in declaration order when inspected with reflection, but their offsets could jump around within the struct, yet well behaved reflection-based code should be agnostic to this change.

Post reply on HN