5 ms·
> You're discounting the cost of the GC approach necessarily having more live objects at once. No, by live objects I don't mean the objects that currently exis
by openasocket 11y ago
> You're discounting the cost of the GC approach necessarily having more live objects at once.
No, by live objects I don't mean the objects that currently exist in the heap, I mean the objects currently reachable by the program, which is the same whether or not you are using GC. But you are right that GC will use much more heap on average than manual allocation.
I agree that you can probably always beat GC using manual allocation if you are sufficiently smart about it. For instance, if you have some data structure you know is going to allocate a bunch of small objects, and none of the small objects can outlive the data structure, you can have that data structure create a special slab just for its objects, so when you free the data structure you just need to call one free() on the slab instead of a bunch of free() calls on every object.
- deleted 11y ago[deleted]
- scott_s 11y agoI don't you need to be particular smart using manual memory allocation to beat GC. You just need to free your memory once you're done with it. Consider: for (size_t i = 0; i < 100000000; ++i) { size_t* val = malloc(sizeof(size_t)); *val = i; free(val); } Decent memory allocators will just keep reusing the same memory location over-and-over. All 100 million calls to malloc and free will be fast-path calls. GC-based schemes will most likely not be 100 million fast-path allocations. Yes, this is a contrived example, but freeing memory as you no longer need it is common in languages with manual memory allocation. It is also harder, more error prone, and quite often leads to nasty bugs, but it's usually faster. I think you're reasoning about just the cost of the frees versus garbage collection cost. But by ignoring the extra memory required on the heap, you're also ignoring the extra slow-path allocations.
- openasocket 11y agoI believe in this example GC will actually perform better! With GC, this code will fill up the eden slab with a bunch on these integers. When there is no more space in the eden slab, the GC makes a new slab (one malloc() call), copies over any live data by checking what's accesible from the stack (there are no pointer saved on the stack except for the current val) and the old slab is freed (one free() call). So GC calls free 100M/(number of ints we can fit in the eden slab) times, as opposed to the manual case which calls free 100M times. And, just like the manual case, the GC's malloc and free will be fast-path calls (our eden slab will essentially just switch back and forth between two different memory locations). And the cost for tracing out the live variables here is essentially nothing (10-100s of nanoseconds) since nothing is referenced in local variables. The only cost you have to pay with GC in this situation is increased heap usage and, consequently, a larger cache footprint.
- scott_s 11y agoI would be shocked if the GC performed better. I think you are severely over-estimating how expensive the fast-path for memory allocations are. For this example, decent memory allocators will execute on the order of a dozen instructions for each malloc and free call - no need to allocate new slabs in the allocator, no need to free old ones, no interactions with the operating system or even the internal slab allocator. Calling free is not what is expensive. It's what we do when we call free. GC has to do more work because it has to figure things out. Manual memory allocation does not need to spend any instructions figuring anything out because the programmer has already done that. As I have said before, decent memory allocators will keep returning the exact same address for each malloc call in this situation. That is the ideal situation for this case. Also keep in mind that larger heap sizes don't come for free - that means more page faults, which will slow you down.