3 ms·
A lot of structs or, say, slices in Go escape just as much. Or the interface implementations which get boxed. You have to pick any workload that is even remotel
by neonsunset 2y ago
A lot of structs or, say, slices in Go escape just as much. Or the interface implementations which get boxed. You have to pick any workload that is even remotely allocation/collection throughput sensitive and limitations of Go's GC become immediately apparent. What is not immediately apparent is that Go offers limited avenues to address this.
It's a good design for its purpose especially given that it is much simpler and self-hosted when compared to Java's and .NET's GC impls, but assigning the attributes to it that never were there nor were pursued by its authors is just bad, but sadly common with some here, look.
- lossolo 2y agoIs your opinion grounded in theory or practice? As a practitioner running Go in a high-volume, low-latency environment with tens of millions of allocations per second, we haven't found GC to be a problem. The median pause time is actually around 130 microseconds per collection. We're using Go in network services. Maybe your use case differs from ours, but at the end of the day, you pick the right tool for the job, and programming languages are just tools. We believe we picked the right one.
- neonsunset 2y agoThis is a rather strong claim not supported by real-world data on how Go’s GC behaves under load. Which, in fact, isn’t the first time for your account as search across the discussion history indicates so you do you. Sustaining very small allocations with median period of say 66ns (and I’m going to be charitable here) is not that difficult with single-threaded workload. Things get interesting as it becomes multithreaded and object graph becomes more complex. As I said before, on any microbenchmark that is meant to evaluate allocation+collection throughput Go performs worse than the competition and there is no workaround to this because that’s just how its GC is designed: https://benchmarksgame-team.pages.debian.net/benchmarksgame/performance/binarytrees.html https://benchmarksgame-team.pages.debian.net/benchmarksgame/... And this is without even involving any sort of cross-thread data sharing between the allocations. It’s okay to praise things you like and criticize those you don’t, but it can be kept within the realm of actual pros and cons of a particular technology.
- tinco 2y agoI think you're missing the parents' point. I think you and the parent agree would that for these general purpose benchmarks a more advanced garbage collector would win out over Go's garbage collector. But the point that has to be made is that those simply don't apply to the sort of application Go is built for. If your Go application is not a network service, and frankly, is over 10.000 lines of code, you're not using Go for its intended purpose. An application with a complex object graph seems exactly like the sort of application that shouldn't be written in Go. As an aside, it's a bit weird that you'd react to their citing of real-world data with a dismissal and a sort of ad hominem, and then proceed to cite a microbenchmark of precisely the sort of thing people shouldn't be building in Go. Do you have a microbenchmark that disproves that the Go GC can get 130 microsecond collect times while under tens of millions allocations per second load?
- neonsunset 2y ago> If your Go application is not a network service, and frankly, is over 10.000 lines of code, you're not using Go for its intended purpose. An application with a complex object graph seems exactly like the sort of application that shouldn't be written in Go. It seems the industry is moving to adopt Go for this exact use case, unfortunately. Not sure if I read the parent's argument the same way however, as what I'm objecting to is the continuous assignment of capabilities to language implementations that they don't have. On 130us - pause time is frequently quoted in Go discourse but it is somewhat misleading as this omits the "front-loaded" nature of its design. If you spend >5x CPU time executing GC code than more, err, pause-focused designs and have lower throughput, then those 130us no longer exist in a vacuum. It is more accurate to specify that Go favors the sweet spot of "smaller" amount of work performed by a single node and focuses on maintaining consistent latency for light to moderate allocation rates with low heap size. In a way, .NET's GC is evolving in a similar direction but attempting to maintain the throughput or ensure the loss is within single digit %.
- lossolo 2y ago> It is more accurate to specify that Go favors the sweet spot of "smaller" amount of work ... focuses on maintaining consistent latency ... It seems like you wrote the same thing that I wrote, but you used different words for it. What you described is basically I/O-bound workloads with a particular emphasis on latency—in other words, the category of networked services, which is precisely what Go was created for and why we use it. > In a way, .NET's GC is evolving in a similar direction but attempting to maintain the throughput or ensure the loss is within single digit %. Let me know when it finally evolves! I would like to try it out for some of our smaller services then. When we started building our core services in Go many years ago, .NET Core wasn't as good in terms of network throughput, latency, and handling multiple I/O-bound tasks simultaneously. I was actually evaluating it back then too. PS. From one of our production nodes from the last 24h: GC Pause time: Min: 85.1 µs Max: 343 µs Avg: 150 µs https://imgur.com/0HjUyM7 https://imgur.com/0HjUyM7