Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This is a common misconception that stems from a misunderstanding of why manual memory management is "fast". It has nothing to do with the actual process by which you request and release memory. Manual memory management being faster than GC is a function of controlling memory layout, which is one of the (very) few low hanging optimization fruits that mortals like you or I can do on contemporary CPUs. It also has to deal with the fact that dynamic allocation is slow, and being able to get it out of the way with a single allocation in the first moments of a process's life is an immensely important tool in the optimizer's toolbelt.

> The anemic abstractions provided in the language and the tiny stdlib means it takes a lot of work to achieve something

Which has the additional effect of forcing you to be a bit smarter about how you do things, to be less wasteful. It forces you to contend with everything you want to do, to consider it and the cost associated with it. Built-in, general-case abstractions are nice when under time constraint and hacking something together, but it doesn't make for good software. Not only is it almost guaranteed to be slower than a properly constructed purpose-built solution, but it also removes your view from thinking about the cost of every single thing you're doing. It makes it easier and attractive to overuse abstractions, to over-engineer solutions, and to approach problems from a standpoint where you simply throw the kitchen sink at the problem because that's the only thing you can think of.



>It has nothing to do with the actual process by which you request and release memory. Manual memory management being faster than GC is a function of controlling memory layout, which is one of the (very) few low hanging optimization fruits that mortals like you or I can do on contemporary CPUs.

That is kind of misleading. The difference is that C and Rust support stack allocation, which is essentially an arena style allocator integrated into the language. What the fancy pointer bumping GC runtimes do, the stack does by default. The problem is that escape analysis is difficult and it is difficult to prove that an access to memory on the stack is safe without fundamentally changing the language like Rust does. It gets worse on the heap, where you can have runtime determined ownership.

C programmers like their doubly linked lists, but when you think about it, it is actually kind of a difficult problem to formalize and analyze in its full generality.


It's not about the call stack (which I don't think is that special), but about control over layout of data structures. Languages like Java introduce lots of indirection and "object overhead".


> Manual memory management being faster than GC is a function of controlling memory layout

Control over memory layout and manually allocating and freeing memory are orthogonal issues.

I can optimize memory layout in Java too by using primitive data types instead of pointer-chasing objects, or structs of arrays vs array of structs type of things in order to improve access patterns. I can't control alignment and padding, except indirectly, thats true, but that is not what people mean when they say "manual memory management". Rust gives you control over memory layout, but has "automatic" memory management.

> forcing you to be a bit smarter about how you do things, to be less wasteful

Yes this is what I meant.


Love your words. 1000 upvotes if I could.

For balance, the faster machines get, the more problems are most effectively solved by throwing the kitchen sink at them.


Sometimes wasting a perfectly good kitchen sink on a small problem gives you two bigger problems.


There are some performance advantages a garbage collector can have over manual memory management. If you're just calling malloc/free or, in C++, calling new/delete in constructors/destructors (or using a class that does so, like std::vector), and nothing special, the garbage collector is probably allocating memory faster.

> controlling memory layout

Garbage collectors can compact active memory into one contiguous location and adjust the active pointers to point there instead. You can't do this in a language like C, because you can have arbitrary pointers to anything, and there's no runtime indication of what's a pointer or just an integer. You simply have to prevent memory fragmentation in the first place, which also complicates the logic of the program.

For faster allocation in C, arena allocation based on object lifetimes can be used [1]; in generational garbage collectors, you get similar benefits, but it's just done automatically. In fact, in that linked paper, they found that lifetime-based arena allocation improved the speed of their program (a C compiler) at the cost of increased memory allocation compared to naïve malloc() and free(), which is exactly what garbage collection does.

As a result of compaction, memory allocation with garbage collection is just a pointer bump in the best case, whereas allocation with just malloc usually requires searching a free list or a tree.

[1]: https://www.cs.princeton.edu/techreports/1988/191.pdf




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: