8 ms·
It's interesting how we have come full circle from "Reference counting is the worst of two worlds [manual and GC] and will always be slower" to now "Well, we al
by habibur 4y ago
It's interesting how we have come full circle from "Reference counting is the worst of two worlds [manual and GC] and will always be slower" to now "Well, we all know it's actually faster." in like 10 years.
- pclmulqdq 4y agoIts actually usually slower than both manual memory management and GC. It's only coming back now because people are finally learning how to make memory allocations large and rare. This blog post is an answer to: "Tell me you haven't learned about cache coherence without telling me you haven't learned about cache coherence."
- rwmj 4y agoHiring would certainly be a lot easier if more people were to make bold, completely wrong blog postings like these. I could immediately give my negative recommendation without the time and hassle of a phone interview.
- deleted 4y ago[deleted]
- pclmulqdq 4y agoI completely agree, but before we scare people away from blogging too much, I will say that my big problem with this post isn't the lack of knowledge, it's the willful ignorance and lack of humility. It's clear that the author doesn't really understand the position they are trying to argue against.
- OskarS 4y ago> Its actually usually slower than both manual memory management and GC [citation needed] You and the blog post are arguing opposite things, and neither of you have shown any evidence. I get that you're arguing that reference counted objects are bigger (to store the reference count) and/or might use double indirection (depending on implementation), which are both bad for caches. It's not a bad argument. But the counter-argument that the blog posts makes is persuasive as well: it's expensive running a GC that scans the heap looking for loose objects, and reference counting does not need to do that. GC is also "stop-the-world" as well unpredictable and jittery in a way reference counting is not. My instinct is that reference counting is actually faster (which matches my personal experience), but really, this is not an argument you can solve by arguing in the abstract, you need actual data and benchmarks.
- adgjlsfhk1 4y agoThe blog post is largely incoherent for several reasons. 1. It recommends 8 byte counters. This implies making every object in your programming language 8 bytes bigger which is pretty much a non-starter. Almost everyone that actually uses reference counting uses more like 2-4 bits. 2. Reference counting can have arbitrarily long pauses as well. If you decrease the reference count of an object to zero, you have to decrease the reference count of all referenced objects, which can do significant amounts of work (specifically, it will do a lot of work in the cases where regular GC does almost no work). 3. The blog states that atomic writes are "basically free", but that ignores the fact that in multi-threaded code, atomic writes can actually be fairly expensive since each one requires communication between every thread (This is why python still has a GIL) 4. Because no one uses 8 bytes for the count (since you don't want to double your memory consumption), you still need a GC anyway to deal with objects that get too many references.
- OskarS 4y ago> Almost everyone that actually uses reference counting uses more like 2-4 bits. Here is an incomplete list of languages which use 8 bytes for their reference count on 64-bit computers: 1. Rust, in Rc<T> and Arc<T> 2. C++, in std::shared_ptr<T> 3. Objective-C, in NSObject's retainCount 4. Swift, because of Objective-C 5. Python, where reference count is Py_ssize_t These were literally the first languages i thought of, they all use 64-bit types. I would argue since reference counting is much rarer than GC, these make up the bulk of reference counting use in the real world. "Almost everyone", huh? It's a bit rich accusing the author of being "almost incoherent" and then say this. > Reference counting can have arbitrarily long pauses as well. Deallocating can take arbitrarily long, but deallocation does not stop-the-world. It stops the current thread, which is a huge difference. Malloc can take arbitrarily long as well, it's not like it's wait-free. In addition, the GC pauses in regular programming languages are frequently orders of magnitude longer than deallocation. You would have to deallocate an enormous tree of object at the root for this to be an issue. And GCs have to do that as well, in addition to their regular stop-the-world pauses. This argument is just irrelevant. > The blog states that atomic writes are "basically free", but that ignores the fact that in multi-threaded code, atomic writes can actually be fairly expensive since each one requires communication between every thread (This is why python still has a GIL) First off, this is not why Python has a GIL, but lets leave that aside. Atomic writes are more expensive than non-atomic ones, but they are not slow operations in the grand scheme of things. If you properly implement acquire-release semantics, they are not even that slow under high contention. Compare this to a GC which literally STOPS ALL THREADS, it's nothing. > you still need a GC anyway to deal with objects that get too many references. This is just silly. In languages which has both reference counting and traditional garbage collection (e.g. Python), they do it to avoid reference cycles, not because objects get "too many references". That is a ridiculous statement. In fact! I just realized we do have data for which is more performant: this article describes how Instagram turned of GC for Python and just relied on reference counting. They gained 10% increase in performance: https://instagram-engineering.com/dismissing-python-garbage-collection-at-instagram-4dca40b29172 https://instagram-engineering.com/dismissing-python-garbage-... I think it's still an open question if reference counting is always faster than GC, but I do not believe you have the technical expertise to evaluate such a claim. Your comment is four paragraphs long and riddled with factual errors. If you want to be convincing, show data that is better than that Instagram case study.
- hinkley 4y agoHas anyone done a good paper on how memory bank affinity for processors affects these costs?
- flohofwoe 4y agoExcept that "refcounting is faster than a GC" is mostly a myth, both are equally bad if predictable performance matters.
- klodolph 4y agoLooking at metrics from Go's garbage collector, what else do you want? GC pauses are damn low, and you'll see numbers in the sub 500μs range. If I needed hard-realtime, I would avoid allocation entirely.
- kaba0 4y agoJava's ZGC is the state-of-the-art on low-latency, high-throughput GCs, so it might be a better example. Also, there are hard-real time JVMs -- but hard-realtime really doesn't mean what people often believe. These systems are usually much slower - that's the price they pay for having a fix higher bound on some latency requirement.
- klodolph 4y agoYes, the Java stuff is better. I picked Go as an example because you get very low pause times with no configuration. It's really common for Java apps to be misconfigured, especially when people start throwing GC tuning options at them--people who often don't understand what those tuning options do. For example, "the pauses are too long, let's increase the heap size".
- kaba0 4y agoYeah I agree. Just for anyone who might stumble upon this, if you go with the default G1GC, pretty much just leave it as is. IF you experience some problem that you assume is due to the GC, you may try changing the singular config option of target pause time, which will tweak the GC on the latency-throughput spectrum (which are opposite ends of the same axis)