4 ms·
In what way is RAII a burden? Genuinely curious, since I find it quite easy to work with.
by pptr 3y ago
In what way is RAII a burden?
Genuinely curious, since I find it quite easy to work with.
- bsder 3y agoWhen deinit occurs far away from init, RAII starts adding a lot of cognitive burden. Once a thing "escapes" from where it was created/initialized, it suddenly has a life cycle. Everything in that thing shares in that life cycle. It can cross multiple threads during that lifecycle. When that thing completes its life cycle is unbounded. Consequently, you can run out of intermediate resources (memory, file descriptors, database transactions, etc.) even though you would have enough if they could be reclaimed right now but you can't prove that you can do so. This is one of the reasons why Rust exists and why it defaults to move semantics. Everything that you need to deallocate a thing is present at all times--you own the thing. If you borrow the thing, you cannot deallocate it. Life is good. Sorta ... Sometimes somebody else owns and controls the thing. Sometimes you initialize once and then everything is read-only from that point forward--synchronizing on a single runtime event gate. Not everything is memory and has bounded reclamation time. Sometimes things want different allocators and allocation strategies--you will reclaim some things every 16ms and some things very rarely. Sometimes you want to allocate on one thread and deallocate on another. A lot of these don't fit RAII all that well because there is a time dimension to them rather than just space. But then you have things like "reference counting" that's just an absolute nightmare to do without some mechanism like RAII. It's easy to always increment/decrement the reference count, but your performance is terrible--you need compiler elision of those for performance. I tried implementing a reference counted Scheme in C and Zig, and I wanted to blow my brains out from hunting all the reference counting bugs while C++/Rust would have been a breeze. Perhaps I just made a terrible architecture. ¯\_(ツ)_/¯ I'm not sure what the solution should be. Rust took a very hard line about not paying runtime costs for things you don't use--that means lots of compile time effort, debug runs that are glacially slow, and blocking certain standard idioms behind "unsafe". In reality, I'm actually willing to pay more at runtime than I thought as long as my runtime is deterministic. I'm willing to pay a bit at runtime to get a compiler that's two orders of magnitude faster whose debug code is only 25-50% slower than standard. I'm willing to pay at runtime for null and bounds checks. I've got a zillion cores doing nothing--giving up 10% to get a nicer language is perfectly acceptable to me.
- pptr 3y agoNormally you allocate on the stack, so the local function owns the object. You pass (unowned) references to any function you want to call. Those functions are not concerned about RAII for those references, since they don't own them. If you want to pass ownership to a different function/thread, you move the object. It's the owner's responsibility to run all the destructors once the caller deletes the object, which RAII does for you. Granted, this can get undeterministic with the reference counted shared_ptr, since only the last owner of the reference will actually delete the object. I actually really like RCU for shared access: https://en.m.wikipedia.org/wiki/Read-copy-update https://en.m.wikipedia.org/wiki/Read-copy-update Deallocation is always done in a background thread and there is no synchronization when reading. I'm not sure what public libraries are available, unfortunately (I've only used our internal rcu library). If you want to make efficient use of your zillion cores, you need to make sure single threaded performance is excellent ;) See Amdahl's law. But I agree, development velocity is an important factor to consider when choosing languages.
- bsder 3y ago> I actually really like RCU for shared access: https://en.m.wikipedia.org/wiki/Read-copy-update https://en.m.wikipedia.org/wiki/Read-copy-update Deallocation is always done in a background thread and there is no synchronization when reading. I'm not sure what public libraries are available, unfortunately (I've only used our internal rcu library). I do like "eventually consistent" data structures like this where you either see the old one or the new one consistently. However, at that point, you've basically created garbage collection as you've lost your deterministic behavior. (It will get collected--sometime, maybe). They mention the Linux kernel and reference counting specifically, so I'll look at this more in depth. > If you want to make efficient use of your zillion cores, you need to make sure single threaded performance is excellent ;) See Amdahl's law. Erm, that's NOT what Amdahl's law says. "The overall performance improvement gained by optimizing a single part of a system is limited by the fraction of time that the improved part is actually used" Cores mostly spend their time waiting--so improving single core performance isn't a great benefit. In fact, going backwards to lot of simpler cores but lots more cache per core is probably a better benefit. Apple got this. They made the memory system a giant cache and got a huge performance boost.