3 ms·
I've heard these arguments many times before, about how in theory GC'd languages can have less memory allocation/deallocation overhead. This theoretical benefit
by gizmo 11y ago
I've heard these arguments many times before, about how in theory GC'd languages can have less memory allocation/deallocation overhead. This theoretical benefit never materializes, though. This is because in a language with garbage collection the programmer barely thinks about memory at all which results in millions of tiny allocations happening all over the place and that wrecks performance. In contrast to languages with manual memory management where programmers use strategies that minimize the headache (memory leaks and doubly-freed data) of manual memory management which results in there being vastly fewer memory operations.
Where in a dynamic language people are tempted to use write things like `people.pick('name').capitalize().sort()`, where containers of objects are created at every stage of the pipeline just to be discarded moments later. In C you wouldn't write code like that. Or if you do, you simply allocate a single block of memory beforehand that's large enough for the entire operation and you reuse that memory block at every stage of the pipeline. In C more memory efficient code is less work to write and easier to reason about.
So it really shouldn't be a surprise that in practice GC'd languages like Java are dogs in terms of performance, nor should we expect this to change no matter how clever the garbage collectors get. The "sufficiently intelligent garbage collector" is a creature just as mystical as the "sufficiently intelligent compiler".
- qznc 11y agoI still hope for D here. (Maybe C# as well, but I don't know C# well enough) The D community encourages `people.pick('name').capitalize().sort()`. They call it Component-Programming and they make it fast. See [0] for a more realistic example. The trick is to turn the methods into (lazy) iterators (streams in Java land) with static dispatch. Now the compiler can inline everything and optimize it like a C-for-loop. The problem is that the D garbage collector needs more engineering effort to be on Java level. For example, it is not precise or concurrent yet. [0] http://wiki.dlang.org/Component_programming_with_ranges#Formatting_a_Year http://wiki.dlang.org/Component_programming_with_ranges#Form...
- vvanders 11y agoRust does something very similar, albeit with lambdas for extra syntax goodness.
- Manishearth 11y ago(to be clear, lambdas aren't necessary for this pattern to work in Rust, just that the stdlib has generic iterator adapters that are created using lambdas letting you do a myriad of things with a single function. Rust lambdas (closures) are on the stack, so this usually gets optimized down to a for loop or something)
- xenadu02 11y agoThis is nonsense. Games using Unity think about it all the time and that's using C# and a GC runtime. (Here's a dirty little secret: C++ games use object pools too and for the same reason! You can't afford to take the malloc/free overhead in the middle of rendering a frame, any more than you can afford a GC pause!) Again looking at C# (or Swift's Lazy collections, or Java streams) your example isn't quite correct. You can absolutely get single-pass lazy evaluation of the elements, or at the very least avoid any temporary intermediates. The .NET runtime has various GCs, some of which are concurrent GCs that can do thread-local pauses without pausing the entire process, as well as doing extremely short pauses (a few ms) but otherwise running concurrently with other threads doing allocations. The GC even does compaction, avoiding the memory fragmentation problem that has driven many a C programmer crazy. (There are other approaches like V8's JS GC.) The idea that GC has to be a dog is nonsense. It has the benefit that allocation is just adding sizeof(object) to a pointer, making allocation trivial. It has the runtime overhead of occasionally needing to pause things for memory safety reasons, depending on the specific implementation. It also has a higher high-water mark for memory usage. It has the human overhead of reasoning about living roots and avoiding unnecessary object churn (though that is true of any system). Reference counting has runtime overhead of incrementing/decrementing reference counts, and human overhead of manually breaking retain cycles and remembering to properly increment/decrement the counts (unless using things like ARC which insert the calls automatically). malloc/free has runtime overhead to manage slabs and free lists, plus fragmentation problems under some workloads. It has a massive human overhead of making the programmer remember to track and free the allocated memory perfectly or risk leaking or corrupting memory, potentially leading to remote code execution in the worst case. These are all tradeoffs with costs and benefits.
- kabdib 11y agoMy experience with XNA (a C# based gaming environment) was that allocating memory and incurring GC was death. So you bent heaven and earth to avoid making garbage. I haven't used Unity. It's probably better. (XNA was essentially abandonware a couple years after Microsoft hatched it -- a typical myopic lifecycle and a good reason why you can't trust MS with your platforms unless they have a really strong money story behind them).
- 11y ago
- catnaroek 11y ago> things like `people.pick('name').capitalize().sort()` With substructural types, the programmer can tell the compiler that a specific value is meant to be used only once. For instance, the `self`-arguments to `.capitalize()` and `.sort()` can be modified in-place, if only the compiler actually knew that it won't be used elsewhere. > The "sufficiently intelligent garbage collector" is a creature just as mystical as the "sufficiently intelligent compiler". The real myth is that a “sufficiently intelligent compiler” can figure out what you really mean without you telling it. Compilers are often better than programmers at identifying opportunities for optimization, as well as correctly carrying them out, but the validity of these optimizations often depends on preconditions that compilers can't figure on their own - the programmer must tell them. Fortunately, the science of systematic, reliable and truthful communication between programmer and compiler already exists: https://en.wikipedia.org/wiki/Type_theory https://en.wikipedia.org/wiki/Type_theory
- lmm 11y ago> Where in a dynamic language people are tempted to use write things like `people.pick('name').capitalize().sort()`, where containers of objects are created at every stage of the pipeline just to be discarded moments later. In C you wouldn't write code like that. Or if you do, you simply allocate a single block of memory beforehand that's large enough for the entire operation and you reuse that memory block at every stage of the pipeline Right, so compare like with like. If you write Java in a style where you're thinking carefully about performance, you can get very good performance - probably faster than you would spending the same amount of time writing C. If you write Java in a style where you think a little bit about performance, and then spend a bit of time profiling your application after, you tend to end up with something faster than your first effort in C and taking a lot less time to write as well. But for some reason people seem to want to compare the dumbest thing you could write in Java that would work at all with the dumbest thing you could write in C that would work at all, regardless of the fact that it would take much more programmer time to produce the latter.