13 ms·
Modern garbage collection: Part 2
- brighteyes 7y agoThe technical detail in this article is excellent! A great read. But I think it would have been an even better article without the negativity about Go and how the author thinks "the Java guys are winning" in his words. That felt a little petty.
- randomidiot666 7y agoWhy? Did he hurt Go's feelings? It's a programming language, not a child. I prefer his honest assessment of their relative strengths and weaknesses.
- brighteyes 7y agoI just think our industry would be a better place if we didn't try to tear down other technologies, say stuff like "X sucks", etc. But I agree that friendly competition is a good thing! I feel this article was a little past that, though.
- randomidiot666 7y agoBut he didn't say that. That's your own ridiculous emotional projection. The article contains very thoughtful criticism backed by detailed analysis. I don't think you even bothered to read the full articles (there is a part 1). The final sentence was, "Overall, it looks to me like the Java guys are winning the low latency game.", and that's a fair conclusion.
- brighteyes 7y agoMaybe you're right, I might be too sensitive on this matter. Thank you for your thoughts.
- randomidiot666 7y agoI am always right. Go sucks and Java wins. (just kidding)
- ncmncm 7y agoHalf right, anyway.
- throwaway34241 7y agoBased on the facts stated in the article (part 2), it looks like (years later) Java now has a new garbage collector available (ZGC, still labeled experimental, not used by default) [1]. It's an order of magnitude lower latency than current default collector, but it still has an order of magnitude higher latency target (10ms vs 1ms) than Go. I'm sure there are many good reasons for the trade offs made and that's a huge improvement, but in light of the basic numbers I'm not sure that "the Java guys are winning the low latency game" is, in fact, a fair conclusion. Go introduced a new GC a while back with dramatically lower latency numbers, not only compared to the previous Go GC but any other commonly available GC in any language (excluding exotic commercial ones like Azul). Neither the old or new Go GCs are particularly sophisticated compared to the highly developed ones in the Java ecosystem. The author of these articles seems to take objection that the latency numbers were achieved not by magic or pure GC implementation genius but by simply optimizing for latency, which involves some trade offs. I don't think this negates the usefulness of having a low latency GC available though, although I can understand how it might be frustrating to see a project getting a lot of attention for what feels like a lesser intellectual achievement. Obviously not all projects even require low worst-case latency, monolithic apps will be less sensitive, applications waiting on 10 other services will be more etc. Some apps the CPU isn't a bottleneck either. It's just another trade off where Go is prioritizing some things. There's even other factors not mentioned in the article like a compacting collector might make calling C functions more complex since it needs object pinning. [1] There's also another GC mentioned that's less talked about in the article, Shenandoah, that requires patching the JVM and introduces memory overhead to every object for a forwarding pointer. It was hard to find numbers, but it looks like the latency target for this GC is also in the 10ms range (http://clojure-goes-fast.com/blog/shenandoah-in-production/ http://clojure-goes-fast.com/blog/shenandoah-in-production/).
- denulu 7y ago> It's an order of magnitude lower latency than current default collector Believe it or not, the current default GC is G1GC, which has a target (and quite common) pause of 300ms by default. > Go introduced a new GC a while back with dramatically lower latency numbers Or maybe just like Azul's numbers from 2005? http://big-elephants.com/2018-09/unexpected-gc-pauses/ http://big-elephants.com/2018-09/unexpected-gc-pauses/ https://www.usenix.net/legacy/events/vee05/full_papers/p46-click.pdf https://www.usenix.net/legacy/events/vee05/full_papers/p46-c... > Shenandoah, that requires patching the JVM The page you linked to says "How do you get Shenandoah? This garbage collector has officially become part of JDK only since version 12 and is available in AdoptOpenJDK 12 builds." > and introduces memory overhead to every object for a forwarding pointer. The first version did, Shenandoah 2.0 does not https://shipilev.net/talks/jugbb-Sep2019-shenandoah.pdf https://shipilev.net/talks/jugbb-Sep2019-shenandoah.pdf
- uluyol 7y agoExcept that it isn't necessary nor is it completely honest. Go and Java take different approaches here, but the article focuses on the merits of the Java approach and the downsides of the approach taken by Go. Example 1. The article talks about compaction and generational collection as being Good Things(TM), but it doesn't talk about the costs associated with them. Looking at the linked Go article, these approaches suffer from high write barrier overhead. For Go, this isn't worthwhile because escape analysis allocates many young objects on the stack (which btw is effectively bump-pointer allocation) so trying to further reduce GC overhead by increasing the overhead of every pointer write is just not worth it. It may, however, be the right trade-off for Java. Example 2. Java's many tuning parameters means that programmers who care about performance have to choose the right GC and tune it. If better GCs come out or tweaks to the algorithms are made, these configurations have to be updated. In contrast, Go programs gets these benefits for free. The best approach seems to be to offer a small number of high-level knobs, but it's hard to determine what those are, leading to the two (suboptimal) extremes you see with Go and Java.
- randomidiot666 7y agoYet another person who only skimmed the article and hasn't read Part 1, which explains GC tuning tradeoffs and addresses the two examples you mentioned. Part 1 debunks the dishonest marketing from Google around Go's GC when it was originally released, and discusses the tradeoffs that weren't disclosed.
- pcwalton 7y agoThese are common misconceptions. > Example 1. The article talks about compaction and generational collection as being Good Things(TM), but it doesn't talk about the costs associated with them. Looking at the linked Go article, these approaches suffer from high write barrier overhead. You need a write barrier no matter what for any sort of incremental or concurrent GC, to maintain the tricolor invariant. Otherwise there is no way for the runtime system to detect a store from a black object to a white object. Typical GCs will fold the write barrier needed for generational GC into the write barrier needed for incremental/concurrent GC, so there is no need for extra overhead if properly implemented. > For Go, this isn't worthwhile because escape analysis allocates many young objects on the stack (which btw is effectively bump-pointer allocation) Java HotSpot has done the same thing for a long time! It's just that in HotSpot escape analysis doesn't really help allocation performance, because the generational GC already offers bump allocation in the nursery. Escape analysis in the JVM does open up more optimizations, though, because it serves as the scalar-replacement-of-aggregates transformation. > so trying to further reduce GC overhead by increasing the overhead of every pointer write is just not worth it. This is only because of their specific implementation. There is no need for increased overhead. > If better GCs come out or tweaks to the algorithms are made, these configurations have to be updated. In contrast, Go programs gets these benefits for free. There is no reason why Java can't do the same by updating defaults. In fact, they often do.
- gsliepen 7y ago> For a video game running at 60 frames per second each frame has 16 milliseconds in which to complete, so a pause of less than a millisecond is not going to be significant relative to other overheads. This is definitely very significant, you just took away 6% of the CPU time available to the game to calculate everything necessary for that frame. And that's if there's only one pause inside each frame. And if you are running close to 100% CPU time, and the pause happens right before the vsync, you might miss the vsync and you will drop a whole frame. This is not only a problem with GC, even with more deterministic memory management, like plain malloc() and free() in C, you can have pauses if the operating system needs to go and find some free memory, update page tables, flush TLB caches and so on. So for games and anything real-time, you probably want to avoid memory allocations as much as possible, and certainly don't want your whole process being paused at random. So if you are stuck with GC, then one question would be: can GC be done for individual threads without blocking other threads?
- deleted 7y ago[deleted]
- dkersten 7y agoYeah, there's a reason games often preallocate large chunks of memory and then not allocate/deallocate during a level, use object pooling and stack allocators. It seems like a strange example to give, when many modern games obsess over minimising delays in all sorts of ways (eg anything Mike Acton talks about: not just cache-friendly data-oriented structure-of-arrays or removing branches, but also eliminating function calls ("where there's one, there's many") and pointer indirection).
- masklinn 7y ago> It seems like a strange example to give, when many modern games obsess over minimising delays in all sorts of ways And the more competitive ones tend to run higher than 60fps too, making the issue even more prominent, meanwhile for those for which it doesn't really matter (e.g. a 2D adventure game)… neither does ultra-low GC latency either.
- 7y ago
- skybrian 7y agoIt's odd how he goes out of his way to make the Go team's decisions seem strange just because they're different. What's going on there? I'm happy that the Go team doesn't want to expose tuning knobs. I've seen a lot of people fiddle with JVM settings without doing the controlled experiments needed to see if it actually helps on a particular machine and I've done that myself. It ends up as cargo-cult programming, like people sharing magic JVM settings on the wiki to allegedly make IntelliJ faster. (It worked for one person!)
- apta 7y ago> I've seen a lot of people fiddle with JVM settings without doing the controlled experiments That's a problem with what the people are doing then, not the JVM. Furthermore, the new low latency JVM GCs only have 2-3 knobs to tune. golang likes to pretend that complexity doesn't exist, and goes for the most simplistic approach, at the cost of things like throughput, code size, speed, code maintainability, etc. The JVM is suited for a much wider range of tasks.
- hu3 7y agoThat's not the impression I get from Go literature at all. Go authors tend to be quite humble about Go being targeted mostly to a specific class of software, read servers. And Go is pretty darn successful at it. The lack of GC knobs is an informed decision within that context.
- apta 7y ago"Servers" refers to a broad range of software. If you're writing a simple app that parses JSON and handles REST, it can be an ok fit. Now if you're writing server software that needs to be high-throughput, then that's where you hit golang's limitations.
- hu3 7y agoHigh throughput servers you say? I'll just leave this here: > How We Built Uber Engineering’s Highest Query per Second Service Using Go https://eng.uber.com/go-geofence/ https://eng.uber.com/go-geofence/ And that was in 2016. Go's GC performance characteristics improved quite a bit since then.
- gok 7y agoIt's telling that of the 15 "things that matter in GC", power consumption isn't mentioned, even in passing.
- markdog12 7y agoThey do mention they may have missed factors in the original article it links to: > As you can see, there are a lot of different factors that go into designing a garbage collector and some of them impact the design of the wider ecosystem around your platform. I’m not even sure I got them all.
- tom_mellior 7y agoWhat should be said about it that's not already covered by CPU and memory overhead?
- cesarb 7y agoPower consumption has some subtle details which make it different from both CPU and memory overhead. For instance, to reduce power consumption it's important to reduce wakeups.
- tom_mellior 7y agoI'd like to learn more, if you're willing to provide something more than vague hints.
- pastrami_panda 7y agoIncremental GC is very promising for games in favour of traditional stop-the-world GC. It's essentially time sliced GC that runs over multiple frames. Unity supports it and if you tweak if correctly for your needs the GC can run while the CPU is waiting for the GPU.
- chrisseaton 7y agoI don't think there are any GCs that do not stop-the-world at some point, except those with special hardware support, are there? Even C4 requires in theory stopping individual threads, and in practice even has a global stop-the-world phase.
- pastrami_panda 7y agoYes you're technically correct but the time slicing makes it practically different. More here: https://blogs.unity3d.com/2018/11/26/feature-preview-incremental-garbage-collection/ https://blogs.unity3d.com/2018/11/26/feature-preview-increme...
- pcwalton 7y agoIt's easy to write a GC that doesn't stop the world at all. A Baker treadmill [1], for example, is a very simple type of incremental collector that can be implemented quickly. (It's a fun exercise, by the way!) If you implement it "by the book", it will have no stop-the-world pauses whatsoever (assuming the mutator doesn't allocate faster than collection can happen). The problem is that a simple GC like this will have low throughput, so it's not worth it. These are some of the most common misconceptions about GCs: that it's hard to write a low-latency GC and that pauses are all that matter. In reality, GC engineering is all about the tradeoff between throughput and latency. [1]: https://www.memorymanagement.org/glossary/t.html#treadmill https://www.memorymanagement.org/glossary/t.html#treadmill
- pcwalton 7y agoAll the GCs we're talking about here are incremental and have been for decades.
- 7y ago
- ryanseys 7y agoI was hoping this was about the infrastructure and systems around collecting human garbage in the modern world.
- symplee 7y agoAnyone have any details on recent developments in the field? For example: Reclaiming raw materials from landfills Machines/robots that can sort recyclable materials from a heap of mixed garbage. Better insulated landfills
- mzi 7y agoIn the area in Stockholm, Sweden where I live we throw "all" garbage (we of course separate glassware, metal, paper, electronics first) in the same bin, but food waste in green bags. I don't know if it's robots that does the final sorting but I would believe so. Less than 1% of household trash ends up in landfills here.
- nwallin 7y ago> For a video game running at 60 frames per second each frame has 16 milliseconds in which to complete, so a pause of less than a millisecond is not going to be significant relative to other overheads. First of all: bullshit. Second of all: bullshit. 99th percentile of 1ms means that 1% of the time you're worse than that. The author says sometimes it's as bad as 8ms. A dropped frame every 15 seconds or so will make your game unplayable. 99th percentile measurements are useless. Tell me how often your users are subjected to any given latency. One millisecond is enormous. My kingdom for a millisecond. If one in ten frames losses 1ms to a GC and one in a thousand frames looses 4ms, that means my budget is 12ms down from 16ms. Thanks, you've sent my expected environment back to 2010. People think they can just say "99th percentile is bad but not terrible" and assume that's the end of the argument but that's not how the world works. 99th percentile frame time happens once every 1.7 seconds. If your web page loads 200 assets (which is basically all of them these days) nearly all page loads will hit worse than 99th percentile for one of those asset loads. 99th percentile analysis is useless.
- ncmncm 7y agoAs we say in fintech, a millisecond is an eternity. (Lately, I have been hearing that a microsecond is an eternity.) Garbage collection promotion is always, fundamentally, an exercise in doublethink. How can an intolerable process be made to seem tolerable, so that my dodgy language which depends on it can be used in place of a mature language which has a robust mechanism for managing all resources, not just memory? It can't. GC is fine for things that don't matter, but things have a way of coming, in time, to matter. Then you have a Problem.