3 ms·
The biggest performance problem with automatic reference counting is that it requires atomic reference counting and/or type system separation of thread-local an
by cwzwarich 11y ago
The biggest performance problem with automatic reference counting is that it requires atomic reference counting and/or type system separation of thread-local and shared data. In a sufficiently multicore scenario the overhead of atomic reference counting becomes unacceptable. If you want to reduce the number of atomic operations you have to do deferred reference counting, which destroys many of the benefits of reference counting, e.g. you can no longer run destructors in a predictable way and have to use GC finalizer semantics.
There is a possibility that better whole-program optimization (potentially combined with annotations for function parameters) can reduce this overhead, but it's a bit of an unknown.
- millstone 11y agoAtomic reference counting is expensive mainly under contention, when the same object is used from multiple threads. Uncontended atomic operations are ~2-3x slower than their nonatomic analogs, which is still very fast. Also I'd expect that GCs should experience a slowdown in the contention scenario, due to write barriers; is it so?
- cwzwarich 11y agoWhile uncontended atomic operations are a lot faster than contended ones, it's hard to avoid the contended case. If you pass a single piece of read-only data throughout your program you will be adjusting its reference count frequently, and those contended atomics will dominate your performance profile. In a concurrent card-marking write barrier you would generally set bytes rather than individual bits so that you can avoid the use of atomic instructions. You still have the problem of false sharing. If this were really a problem, you could emit a conditional write barrier that checks the existing value (with a racy unsynchronized load) before setting the byte. The usual use of a write barrier for generational GC will produce fewer stores than automatic reference counting anyways, because a write barrier is only emitted for potential stores of a reference to a younger generation in an older generation. Automatic reference counting requires reference count adjustments for function parameters and local variables.
- rbehrends 11y ago> Uncontended atomic operations are ~2-3x slower than their nonatomic analogs, which is still very fast. More like up to 20x slower; atomic reference counting has to hit the L3 cache at least. A quick benchmark seems to confirm that. Also, there are still some nasty corner cases with pointers that are shared between threads (because reading/writing a pointer and updating its associated reference count are not a single atomic operation). > Also I'd expect that GCs should experience a slowdown in the contention scenario, due to write barriers; is it so? Write barriers only come into play when pointers on the heap or in static memory are being modified. Reference counting means that you need to update the reference count whenever you assign references, even from one local variable to another (much of the work that Apple's done is to use compiler optimizations to get rid of those that are provably unnecessary). For example, having multiple threads traversing a tree in parallel can generate a lot of contention in the RC case, but none for a GC with a write barrier. Deferred reference counting is closer in performance to GC, but then you have to write most of the machinery of a tracing GC, too.