mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-04 14:38:38 +00:00
`nimDecRefIsLast` always performed an atomic decrement. When the biased count is already zero the destroying thread holds the only reference, so there is nothing to adjudicate and the read-modify-write can be skipped. Soundness: a counted reference can only be derived from the location being destroyed -- which happens-before this destructor unless the program races on that location -- or from another counted reference, whose contribution is already in `rc` and therefore forces the slow path. Observing zero proves no other thread holds a reference and that none can appear. This relies on `--mm:atomicArc` having no collector; ORC and YRC mutate `rc` from a participant that holds no counted reference at all, so the fast path is deliberately not enabled for them. The slow path keeps deciding on the value its own RMW returned. That is what separates this from nim-lang/threading#45, where the "who frees" role was decided from a separate load and the RMW result was discarded, so the role could be dropped by every participant at once. gcbench, -d:danger, median of 21 pinned runs: --mm:arc (non-atomic RC) 0.1310 --mm:atomicArc 0.1742 --mm:atomicArc + this 0.1330 -23.7%, closing 95% of the gap to non-atomic reference counting. gcbench builds its trees with `sink` parameters, so it performs almost no incRefs and the whole atomicArc penalty is decRef traffic. The worst case -- a decrement that always sees rc > 0, so the load never pays off -- measures +1.1%. `-d:nimNoAtomicArcFastPath` restores the previous code path.