https://verdagon.dev/blog/generational-references
Statically eliminating memory operations does seem to be a win though.
With 64-bit counters, that's never going to happen. Alloc/free costs more than a nanosecond, and there are lot more than one element that you will be allocating, but even if you somehow managed to reallocate the same object a billion times a second, it would take over 500 years to run out of indexes.
Practically speaking you could never allocate memory that fast, a memory allocation is going to be well over 1000ns on average.
Then there's the little matter of address space. Pointers on x64 are limited to 47 bits, meaning that if even you had a magical memory allocator with no book-keeping overhead, and all your allocations were 1 byte, you'd run out of pointers first. The actual virtual memory space is limited further on many operating systems, but you're still always going to be well short of 64bits.
Reference counts are not re-used when memory is re-used. And again, even if for some reason you had a global 64bit counter that you incremented on every allocation and never decremented, and you could somehow handle a billion allocations per second, you'd have 585 years before that counter overflowed back to 0. No computer or program can run for that long.
Except when it is: https://threatpost.com/another-linux-kernel-bug-surfaces-all..., fix at https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin...
If you have a multithreaded app doing a lot of communication, that's going to be a lot of cheap allocations happening very fast.
Reducing GC and allocation overhead results in more allocations being done, and pushback against ever-expanding allocation behavior is more of a challenge. Instead of ten other things being a higher priority than judicious data architecture, it's dozens or more.
And even if there was a single counter, multithreading cannot make incrementing a counter faster. Two cores cannot write to the same cache line at the same time. Instead, cache lines need to bounce across cores when you write to them, and this takes such a long time that it turns the time it takes to roll over from centuries to millennia.
Copying references is assumed to be more frequent than allocating and freeing, so this is a win.
(Or possibly the whole region could be discarded once it was running full, and the physical memory recycled at a new virtial address. There's plenty of virtual address space to burn through when not limited to 32 bits)
But I digress. Overhead like increments only matters on hot paths, which are very few. The Python + C stack for ML is a manifestation of this truth.
Having ergonomics of a “regular language” (affects all code) and the ability to optimize for performance (hot paths only) and stay in the same language is what I’m excited about.