4 ms·
I don't understand how you can claim that using a GC does not make a language slower and less predictable. Running a GC takes time, pollutes the cache, and is
by jesse__ 2mo ago
I don't understand how you can claim that using a GC does not make a language slower and less predictable.
Running a GC takes time, pollutes the cache, and is often run at an unpredictable time. Sure, the GC is not necessarily the SLOWEST thing about the language (python), but it's not helping, either.
- xigoi 2mo ago> Running a GC takes time, pollutes the cache, and is often run at an unpredictable time. Isn’t this only the case for tracing garbage collectors? (And even then, not all of them are stop-the-world.)
- pron 2mo ago> Running a GC takes time Yes, but for a moving collector that's less time than it takes to run malloc and free. The interaction of a moving collector with most object is bump allocation when they're allocated (similar to stack allocation) and... that's it. The GC never sees them again, scans them again, or is even aware of their existence (moving collectors don't have a free operation). Overall, moving collectors (but not other kinds of GC) reduce the work of memory management compared to malloc/free. In low-level languages we try to avoid doing a lot of malloc/free not because heap memory management is slow in general, but because that approach to memory management is slow. Moving collectors are an optimisation designed to make heap memory management fast, but it requires that (nearly) all pointers be movable, something that low-level languages can't do because they have constraints that are more important to them than speed (you can't interact with the OS or hardware directly, i.e. without an FFI API, if your pointers are movable, and such direct interaction is the point of low-level languages). That moving collectors (NOT the GC Python has; NOT the GC Go has) can, in principle, make heap memory management cheaper than stack allocation has been well known since the eighties. But until recently they had excellent throughput (somewhat similar to arenas) but potentially long pauses. It was only recently that they were made "pauseless". > and is often run at an unpredictable time How much work malloc and free need to do is also unpredictable, and a modern pauseless moving collector like ZGC spreads the work needed for memory management more evenly than malloc and free. > Sure, the GC is not necessarily the SLOWEST thing about the language (python), but it's not helping, either. There is very little resemblance between CPython's GC and Java. Python's memory management is closer to C's than to Java's. GCs cover such a wide spectrum of algorithms that it doesn't make sense to talk about them as a single category as far as performance tradeoffs are concerned.
- jesse__ 2mo agoOkay. I've written two separate moving collectors for dynamic language runtimes, as well as done significant work in realtime 3D graphics, and what you're saying mostly smells like bullshit. > The interaction of a moving collector with most object is bump allocation when they're allocated (similar to stack allocation) and... that's it. The GC never sees them again, scans them again, or is even aware of their existence I mean .. sure, but, a copying GC eats somewhere on the order of 10% of your total memory bandwidth just copying shit around. I can guarantee that if you use sane allocation strategies (arenas & freelists, pools, whatever) you spend <1% of your total system resources fucking around with memory allocation. > Moving collectors [...] requires that (nearly) all pointers be movable, something that low-level languages can't do Completely false. You have to do some manual bookkeeping in C++, Rust, Zig, whatever, but you can do it, and in fact many commercial GCs do (V8 is a good example). ... The rest of what you said is just empty-sounding claims that I'm not going to address. I looked at the single paper that you linked in another comment, from the 80s, which is hardly relevant on modern hardware. Please, if you're going to make the claims you're making, back them up with hard evidence. I've looked, and the overwhelming majority of papers out there claim that GCs are slow, memory hungry and, generally, a waste of time.
- pron 2mo ago> I've written two separate moving collectors for dynamic language runtimes, as well as done significant work in realtime 3D graphics That's nice. I have ~25 years of experience with large C++ software, including hard and soft realtime systems, and I now work on the JVM. > I mean .. sure, but, a copying GC eats somewhere on the order of 10% of your total memory bandwidth just copying shit around Good moving collectors are designed to copy very little. That's the entire purpose of generations. > I can guarantee that if you use sane allocation strategies (arenas & freelists, pools, whatever) you spend <1% of your total system resources fucking around with memory allocation. In theory. When you're in charge of a >2 MLOC C++ system, maintained by a large team for well over a decade, you find that these optimisations are very costly. Plus, you commonly find large C++ software that needs sophisticated malloc/free allocators for acceptable performance (BTW, some of those allocators are almost the same size, in LOC, as that of ZGC, probably the world's most sophisticated moving collector). That's why, where I used to work and oversee large projects, we migrated pretty much all systems (mostly soft-realtime defence software) to Java from C++ - for better performance. One of the goals of the JVM was to tackle the familiar performance issues that plague large C++ programs. And BTW, using arenas isn't so easy when programs get large and sprawling, or even in general. Zig definitely makes that much easier, though. > Completely false. You have to do some manual bookkeeping in C++, Rust, Zig, whatever, but you can do it, and in fact many commercial GCs do (V8 is a good example). What I said is completely true, but you may have misunderstood it. You can, of course, combine moving collectors with things that expect stable ones in the same process. In fact, you absolutely must, because at some point in the stack you need to talk to the OS and/or hardware, and they expect stable pointers. But you have to have some distinct FFI layer between the two, and the entire point of low-level languages is to be at the lower level. > Please, if you're going to make the claims you're making, back them up with hard evidence. I've looked, and the overwhelming majority of papers out there claim that GCs are slow, memory hungry and, generally, a waste of time. I don't know what you've read, but that is very clearly not the consensus among memory management experts. My "claims" are pretty common industry knowledge, and why the majority of performance-critical large software has migrated away from low-level languages over the past couple of decades, and the trend continues. I'm not trying to change anything, I'm just explaining why the industry is doing what it's doing to those who may not be familiar with large, long-lived software. It's funny, but 25 years ago, the people who doubted the amazing performance-per-effort of moving collectors and JITs were those who (like me) had not used those technologies and were mostly familiar with low-level languages. These days, it's the people who have little experience developing and evolving large and complex software in low-level languages (TBF, there's much less such software written in low-level languages these days) that believe the low-level languages are inherently fast. Having said all that, when programs are relatively small and/or not very concurrent, the effort required to match or beat Java's performance in a low-level language through careful manual optimisation is sometimes worth it. In large software, it gets harder and harder.