back

by dochtman·7y ago·view on hn ↗
Good throughput with GC seems to come with significant extra memory use:

"We compare explicit memory management to both copying and non-copying garbage collectors across a range of benchmarks using the oracular memory manager, and present real (non-simulated) runs that lend further validity to our results. These results quantify the time-space tradeoff of garbage collection: with five times as much memory, an Appel-style generational collector with a non-copying mature space matches the performance of reachability-based explicit memory management. With only three times as much memory, the collector runs on average 17% slower than explicit memory management. However, with only twice as much memory, garbage collection degrades performance by nearly 70%."

https://people.cs.umass.edu/~emery/pubs/gcvsmalloc.pdf

So you're still paying for it, just in a different dimension.

2 comments
> Good throughput with GC seems to come with significant extra memory use

Of course. RAM is the price we pay for GC. Good thing it's so cheap on servers.

BTW, just note that the paper doesn't compare GC (never mind that the algorithms have much improved since then) to real explicit memory management, but to an oracle (i.e. explicit memory management by an all-knowing God). Paying nothing other than for 3x RAM to get performance that's 17% worse than what God could achieve is a bargain, and why GC is considered one of the greatest success stories of modern computing.

Memory you are wasting for GC is Memory you are not using, for example, for caching which directly impacts performance.

Mind you, I think GC is great, but saying that RAM is free is misguided.

FWIW, one mitigating factor is that GC isn't the only approach that wastes memory. You can also end up with memory waste in manual memory management. Due to heap fragmentation, for example, or retaining objects for longer than necessary due to programmer caution or error. I don't (personally) dare estimate their relative sizes, because I'm guessing a huge, perhaps dominant, share of differences in memory usage across languages is driven by features other than GC. Dynamic typing, for example.
I didn't say it's free, only that it's relatively cheap. That RAM can simply be used for caching is incorrect, though. There are very-non-neglible costs to maintaining caches in distributed systems.
It’s not clear what you mean by a distributed system in this context, but RAM is used for caching, costs or no.
My point is that you can't increase the cache size indefinitely to get performance improvements, because you also have to handle cache invalidation. There is an optimal working set.
Using RAM for "caching" sounds extremely wasteful. There could be applications that want to use it for something more useful than a 1% performance improvement.
Caching is way more impactful than a 1% speed up. It is hard to know exactly, because it is impossible to disable on most modern operating systems, but I wouldn’t be surprised if it were an order of magnitude improvement in some situations.

By the way, caching doesn’t prevent RAM for being used by applications. If an application wants more memory, the os can always just evict some of the cache.

This is not only a dated paper (a decade and a half old), but it relies on assumptions that do not necessarily hold in practice.

If you test actual allocation performance on actual current day hardware, you may end up with completely different results, e.g.:

https://github.com/rbehrends/btree-alloc

That's a very fresh repo. I wonder how GHC would fare. I guess I should contribute.
Thanks, but I do not plan to extend this project to other languages. I put it together a while ago as an illustration that conventional wisdom regarding GC cost is not what many people think.

If I were to expand it, I'd look at other allocation patterns rather than more languages; I've already good a fairly good cross-section of garbage collectors and malloc() implementations, so I don't really need any more.

Fair enough. Well, too late, I just hacked together a Haskell version based on the OCaml version. It's naive code, but 16 times faster (0.235s vs 3.818s).

So I expect some GHC specific optimizations are kicking in. Eg GHC is probably smart enough never to construct the whole tree at once. Whether that counts as the kind of static analysis static of object lifetimes someones else in this thread talked about or not, I don't know.

Update: I think it's just common subexpression elimination kicking in..