12 ms·
Go GC: Solving the Latency Problem in Go 1.5
- eggnet 11y agoInteresting. I think the GC pauses are go's biggest problem. Looks like they are tackling this head on with positive results.
- nulltype 11y agoHave you encountered any problems with go GC pauses?
- eggnet 11y agoYes, I have. We used go in a system that had to keep track of essentially large hashes containing popularity / scoring information, in addition to what were effectively routing tables, and ran into >500ms pauses. At scale, it seemed the largest part of the complexity of go was manipulating data structures and code to avoid gc pauses. With sufficient work we may have been able to decrease the pauses sufficiently, but we also ran into raw request per second numbers that were lower than we liked. The direction we have taken was to switch to C++ for this application. Having said that, the gc pauses were the primary reason for the change.
- nulltype 11y agoAh, too bad. Do you know what sort of heap sizes you got those pauses at?
- eggnet 11y agoAbout half a gig resident memory.
- jerf 11y agoIf it's possible, and you're interested in trying, it would be interesting to pull that code out and try it again with Go 1.5, if it's easy. If you've got a C++ solution, I would not suggest under any circumstances short of Go suddenly and frankly mysteriously blowing the doors off of C++ that you switch... I'm just saying it would be an interesting comparison.
- eggnet 11y agoI'll be recommending we try it in a lab with go 1.5, absolutely.
- bjourne 11y agoWhy didn't you just use Java? Hotspot has been optimized for over a decade to make (among other things) gc pauses as manageable as possible. And there are other runtimes (http://www.azulsystems.com/ http://www.azulsystems.com/) specifically optimized for low latency. The poor quality of Go's GC were never a secret.
- omarforgotpwd 11y agoFor many applications, like web backends that may have previously been written in Ruby, Python, or JavaScript, the garbage collector was not noticeable at all. Improvements to the garbage collector target apps that previously would have been written in C with manual memory management, as mentioned in the talk. Not all apps need to be written in C obviously, but this will help bring Go's concurrency primitives to places where the GC was previously a deal breaker, such as eggnet's program.
- BuckRogers 11y agoThat's exactly the awkward ground Go is in and why I stopped using it. It's not really necessary for any company's web backend I've worked on, the existing in Ruby/Python/JS works fine. Yet it can't compete with the other stuff out there, Rust, C(++), Erlang or soon Swift 2.0. Though admittedly, some of these have a dismal concurrency story so I got to hand Go credit where it deserves.
- davidw 11y agoErlang's per-process (Erlang process, not Unix process) GC is pretty good from this point of view. I'm surprised they didn't mention it as something to think about.
- stcredzero 11y agoI've gone through the exercise of enabling something like per-process GC in Go by using multiple processes and the standard Go RPC library. It's easier to get to that point in Erlang, because you're already there by default. Very good low-latency GC is good an alternative to per-process GC, especially combined with Go's ability to let you write to avoid GC pressure.
- andrewchambers 11y agoWhy would you mention it? Go is shares all memory, erlang doesn't. It is a different problem.
- jzelinskie 11y agoActually, IIRC Erlang does have a shared heap, but it is used as an optimization to avoid copying very large objects.
- andrewchambers 11y agoThat doesn't contradict what I said. Go shares ALL memory, erlang may share some sometimes.
- alexbock 11y ago> ...there has been a virtuous cycle between software and hardware development. CPU hardware improves, which enables faster software to be written, which in turn... This is the exact opposite of the experience I've had with (most) software. A new CPU with a higher clock speed makes existing software faster, but most new software written for the new CPU will burn all of the extra CPU cycles on more layers of abstraction or poorly written code until it runs at the same speed that old code ran on the old CPU. I'm impressed that hardware designers and compiler authors can do their jobs well enough to make this sort of bloated software (e.g. multiple gigabytes for a word processor or image editor) succeed in spite of itself. There are of course CPU advancements that make a huge performance difference when used properly (e.g. SSE, multiple cores in consumer machines) and some applications will use them to great effect, but these seem to be few and far between.
- jsprogrammer 11y agoWhy would I want to solve the same problem I was solving last year, but just a little bit faster (or more times, if you prefer)? If it was good enough last year, it's probably good enough today. Today I will solve new problems with even more resources.
- srean 11y agoWas that sarcasm ?
- nulltype 11y agoI'm not sure users really care how many gigabytes their word processor is. How fast it is is probably more interesting to them. And while wasting CPU cycles on abstraction layers isn't a great way to make a super fast program, if the program still runs at 60fps and took half as much time to develop, then maybe they're worth it. Of course, when you end up with some standards-driven monstrosity like a modern web browser, you do seem to have a lot of unnecessary abstraction layers and also it's slow.
- smegel 11y ago
- mseepgood 11y agoBetter images of the plots: https://pbs.twimg.com/media/CJatKFQUkAE5qcR.png:large https://pbs.twimg.com/media/CJatKFQUkAE5qcR.png:large https://pbs.twimg.com/media/CJavrIAUMAAIaq8.png:large https://pbs.twimg.com/media/CJavrIAUMAAIaq8.png:large
- jdub 11y agoWas the presentation more in-depth that this summary? I'd love to read or hear more about the changes.
- zachgersh 11y agoThe summary pretty much covers it in terms of what was presented. There definitely would not have been enough time to discuss the changes they made at length.
- davidgrenier 11y agoAnd with this, I hope we'll see a race for GC pauses improvements.
- arcticbull 11y agoI've worked with garbage collected languages (ruby, Java, Objective-C in the bad old days), automatic reference counting (Objective-C and Swift), in the Rust model and manual reference counting/ownership in C over about 15 years now. Having thought about this a lot, I just don't really understand why people continue to work on garbage collection. Non-deterministic lifecycle of objects/resources, non-deterministic pauses, huge complexity and significant memory and CPU overhead just aren't worth the benefits. All you have to do with ARC in Swift and Objective-C is type 'weak' once in a while (which effectively builds a directed acyclic graph of strong references). With Rust you can get away with just structuring your code in accordance with their conventions. I'm sure this won't resonate with everyone but I think it's time to walk away from GC. I'm curious, is there something I'm missing? The only true benefit I can think of is reducing heap fragmentation; and there must be a better way to address that.
- arcticbull 11y agoTo be clear, modern GCs are truly impressive -- I just don't think that GCs are how we should be addressing memory management. They impress me in the way that, let's say, a Rube Goldberg machine is an impressive way to make toast. I respect the craftsmanship, the creativity, the ingenuity ^_^ but I'd never make toast that way.
- nulltype 11y agoDo you think ARC is a viable alternative? I'm uncertain how the performance compares to GC for most applications.
- arcticbull 11y agoTotally! Or RAII, but that requires much more re-thinking of how your program is put together to do it well. Performance depends on a lot of things. For instance, if you have 'unlimited' memory, GC will be way faster because most likely the collector will never run, and allocations are extremely fast since you can just hack off a slice and hand it out. But in that world, relying on something like a finalizer is impossible since your object will never be collected -- ever. In the real world, memory isn't unbounded. Collection can happen any time and requires traversing large amounts of memory to see what is free and what isn't. If the heap is small, that's fine. If the heap is large, that problem can become truly enormous. Collection can happen at any time, and is totally non-deterministic. On the other hand, ARC's simplest implementation inserts calls to increment and decrement the reference count on your behalf. Plenty of optimizations are possible -- i.e. if the compiler can statically validate the lifecycle of your object at compile time it can cut a lot of these out. And Apple does some amazing runtime magic, too. In return, your objects never outlive their scope, collection happens in-line, deterministically and predictably. Memory usage is much lower and no traversals of your object graph ever happen.
- hit8run 11y ago"Go programs will get a little bit slower in exchange for ensuring lower GC latencies." How much slower are Go1.5 programs compared to their Go1.4 version? Is this relevant for web apps?
- _ph_ 11y agoI am a bit surprised by most of the discussion here so far. Garbage collection has first of all one fundamental advantage: correctness. You are guaranteed never ever to have a pointer to a freed object and that any unreachable object does get freed. For almost all programs that get written, correctness should go over speed. And speaking of speed, unless you require hard realtime behavior, garbage collection can be quite beneficial. A generational GC offers faster allocation times than any malloc based allocator, and the collection of the nursery generation is instantaneous in most cases. ARC has the overhead of counting for each referencing/dereferencing and while it might predictable about kicking in when killing a reference frees memory, the time required to free a given object completely depends on how much objects get consequently freed. Furthermore, garbage collection helps to write clean code, as it is safe (and usually cheap) to allocate memory during a function call and return results referencing the memory. Of course, badly written programs might perform badly with GC - but without the same kind of programs would just be a disaster. And most strategies for efficient memory usage used in non-GC languages (e.g. memory pools for certain objects) can and should be equally used in GC languages.