5 ms·
I spent some time porting the Go garbage collector to Rust, it was most definitely not written in assembly, and was (at the time) known as one of the most high
by tinco 2y ago
I spent some time porting the Go garbage collector to Rust, it was most definitely not written in assembly, and was (at the time) known as one of the most high performance garbage collectors for its particular use case.
And even if there was some assembly at some deep level I hadn't got to yet, you can easily embed assembly in Rust so it wouldn't be an issue.
Also, what sort of problem would there be with cache hierarchies that assembly would be a good solution for? Do you mean just guaranteeing the collection loop runs in L1 cache?
- tsimionescu 2y agoTo be fair, the Go garbage collector is the most primitive GC used in any mainstream language, and lacks most of the advanced optimizations you'd expect from a modern GC. It can't move objects around, it doesn't have generations, it doesn't have good diagnostics (it can't even show you what are the GC roots for its hierarchy in a crash dump), and so on. On the other hand, I'm not sure I've seen ASM used in many GCs, they're not that low level in my experience.
- tinco 2y agoTrue, that's also why I picked that one. It's the only one I knew of that's both in a modern language, relatively small (just under 10k lines at that time) and that had serious production grade performance. It was a bit controversial because they apparently determined that adding the advanced optimizations of modern GC's would negatively impact GC times, so the GC was intentionally kept very simple. I don't know if they maintained that attitude since then.
- lossolo 2y ago> And lacks most of the advanced optimizations you'd expect from a modern GC. It can't move objects around, it doesn't have generations, it doesn't have good diagnostics (it can't even show you what are the GC roots for its hierarchy in a crash dump), and so on. Due to the choices and tradeoffs they made, Go doesn't require most of these 'advanced optimizations.' The way allocation works in Go, along with its use of value types etc, eliminates the need to move objects around or use generations. You can read more about this here [2][3]. > it doesn't have good diagnostics I'm not sure that's true for me personally; I find that it has good enough diagnostics to quickly locate all GC problems using the profiler and GC tracer. You can read more about this here [1]. 1. https://tip.golang.org/doc/gc-guide#Identiying_costs https://tip.golang.org/doc/gc-guide#Identiying_costs 2. https://go.dev/blog/ismmkeynote https://go.dev/blog/ismmkeynote 3. https://itnext.io/go-does-not-need-a-java-style-gc-ac99b8d26c60 https://itnext.io/go-does-not-need-a-java-style-gc-ac99b8d26...
- tsimionescu 2y agoThose articles claim that these problems don't exist in Go, but they don't really explain why, for many of them. The one part that is clear indeed is that the pervasive use of value types in Go reduces the amount of garbage that gets generated compared to Java or C#, and, of course, less garbage means less time spent on GC. The claims about memory fragmentation are less clear though: the first article just says that it's not a problem; and the second one does have a segment dedicated to it, but that segment gets fragmentation completely mixed up with memory locality, an entirely unrelated concept. It later claims that certain allocators are known to not suffer from fragmentation issues, but given the previous confusion, I'm not sure how seriously to take this. It also doesn't say if Go actually uses those allocators or not. As for the diagnostics, I gave a specific example of a very commonly needed diagnostic, identifying GC roots to understand why a memory leak is occurring, that Go simply doesn't provide (someone else suggested a third party package that might help). I am well aware of the basics provided in the article you linked, and they don't even discuss this. For whatever bizarre reason, the Go memory tools don't provide this info (that the GC obviously needs to determine in its operation) - probably another victim in their quest of making it easier to implement the runtime.
- lossolo 2y ago> The claims about memory fragmentation are less clear though As to memory fragmentation, memory allocations are grouped by size, so when objects of the same size are allocated and freed, the memory can be reused efficiently without causing fragmentation. So Go's memory allocator organizes memory into size classes, which are predefined blocks of different sizes. When an object is allocated, it fits into the smallest available size class that can accommodate it. This reduces internal fragmentation (unused space within allocated blocks). > As for the diagnostics, I gave a specific example of a very commonly needed diagnostic, identifying GC roots to understand why a memory leak is occurring, that Go simply doesn't provide (someone else suggested a third party package that might help). I am well aware of the basics provided in the article you linked, and they don't even discuss this. For whatever bizarre reason, the Go memory tools don't provide this info (that the GC obviously needs to determine in its operation) - probably another victim in their quest of making it easier to implement the runtime. Can't you just use pprof to find memory leaks?[1] You can even get a diagram showing where allocations occur and see which lines of code allocate the most memory. In all the cases where I was investigating memory leaks, pprof was sufficient. Do you have any common examples where this wouldn't work? 1. https://www.codereliant.io/memory-leaks-with-pprof/ https://www.codereliant.io/memory-leaks-with-pprof/
- bboreham 2y agoSee ‘viewcore’. (It may not work against the latest Go version, but in principle it shows roots, live objects, etc.) https://pkg.go.dev/github.com/golang/debug/cmd/viewcore https://pkg.go.dev/github.com/golang/debug/cmd/viewcore