3 ms·
Reference counting is neither lower latency nor lower memory overhead than basically everything else. Reference counting requires atomics on every single objec
by bsder 3y ago
Reference counting is neither lower latency nor lower memory overhead than basically everything else.
Reference counting requires atomics on every single object. Per object atomics are quite unfriendly to modern microprocessors.
There is either very little contention with lots of atomics that you don't need or you have high contention with atomics that are blowing out your cache lines repeatedly.
In addition, managing reference counts practically requires RAII semantics and all of the baggage that goes along with that. Doing reference counting in C, for example, is extremely error prone.
- kccqzy 3y agoThe reason Rust has both Arc and Rc types for reference counting is precisely because most of the time when you need reference counting, you do not need thread safety. This is something I think about all the time in C++ when I use std::shared_ptr: it gives me thread safety using atomics but I don't need it. More languages should distinguish between thread safe reference counting and single-threaded reference counting.
- bsder 3y ago> The reason Rust has both Arc and Rc types for reference counting is precisely because most of the time when you need reference counting, you do not need thread safety. Hmmm, I'm curious about your use cases as this is almost precisely opposite my experience. Normally, I regard Rc as a footgun as I've always found that I'm shortly going to have to change everything to Arc.
- jandrewrogers 3y agoI almost never use atomic reference counters, when I use them at all. A major point of using thread-per-core software architectures is that it eliminates almost all atomics even in highly concurrent contexts that may require reference counting to manage resource lifetimes. In these cases, your concurrency primitive is essentially a coroutine, either with or without its own stack. You may have thousands of these executing concurrently similar to as if they were multithreaded and using locks or atomics, but the execution is always on a single thread so locks are unnecessary and atomics can be elided entirely. In the rare cases where atomics are used, there are almost always at most two threads accessing them. Basically, the only place you see atomics are the very thin and lightly accessed interfaces between threads, which are about resource control rather than true thread concurrency. I have used these types of architectures on diverse high-scale server apps. The places I’ve had to use atomic reference counters were always extremely limited and largely outside the hot path.
- bsder 3y agoThen why are you using Rc at all? This seems like "arenas" would be a much better match where you can reclaim everything in one fell swoop as soon as the thread finishes its task. Is the issue simply that Rust until recently didn't support memory allocators other than the standard one?
- jandrewrogers 3y agoThe resource management doesn't fit an arena model, that is an overly simplistic view of what is required. The resources have a lifecycle independent of the references any execution context has with them. A reference isn't ownership, it just keeps the resource from disappearing or pins it in a particular context. Memory allocations may not actually exist in memory. They can be paged to disk in user space because, frankly, the virtual memory hardware on modern CPUs is often incapable of handling large storage and when it does the performance is poor. Sure, you can do better than the default allocators in Rust and elsewhere but that is far from the only reason you might want a reference counter on a resource. DMA requires managing reference counts for threads of execution that only exist in silicon, not in software. Arenas solve none of this. The lifecycle of the resource has nothing to do with the lifecycle of the execution context of a coroutine.
- kccqzy 3y agoI use Rc inside the implementation of data structures such as persistent trees, and also to implement functionality such as Undo. The synchronization happens on a higher level.
- carry_bit 3y agoState-of-the-art referencing counting does not require atomics on every single object; see for example https://dl.acm.org/doi/pdf/10.1145/3519939.3523440 https://dl.acm.org/doi/pdf/10.1145/3519939.3523440