Live data from Hacker News

Reference count, don't garbage collect

kevinlawler.com

41–50 of 415 posts

Re: Reference count, don't garbage collect

#42
post #8

Earlier quoted context omitted.

Its 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."

Hiring 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.

I 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.

Re: Reference count, don't garbage collect

#43
post #15

Earlier quoted context omitted.

> 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 count…

The 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 dec…

> 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 and Arc

2. C++, in std::shared_ptr

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-...

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.

Re: Reference count, don't garbage collect

#44

This debate has gone round and round for decades. There are no hard lines; this is about performance tradeoffs, and always will be. Perhaps the biggest misconception about reference counting is that people believe it avoids GC pauses. That's not true. Essentially, whereas tracing GC has pauses while tracing live data, reference counting has pauses while tracing garbage. Reference counting is really just another kind…

One great example would be a C++ program that runs fast, and then just spends 10s of seconds doing “nothing” while it deallocates shared pointers’ huge object graphs at the end. They really are two sides of the same coin, with tracing GCs being actually correct (you need cycle detection for a correct RC implementation), and having much better throughput. It’s not an accident that runtimes with thousands dev hours are polishing their GC solutions instead of using a much more trivial RC.

Re: Reference count, don't garbage collect

#45
post #5

From what I can see, the myth that needs to be debunked isn't that garbage collection is super fast and easy with no consequences, it's the myth that garbage collection always automatically means your program is going to be spending 80% of its time doing it and freezing for a second every five seconds the instant you use a language with garbage collection. I see far more "I'm writing a web server that's going to hand…

Yes; I have a friend who is part of a small team that wrote a very successful stock market trading gateway in Java. Turns out the JVM's GC can be tuned in very specific ways based on your needs. And there are ways to avoid having to do JVM GC in critical areas of the code as well.

The JVM GC’s are absolutely insanely good. G1 can sustain loads with heap sizes well into TERAbytes.

Re: Reference count, don't garbage collect

#46
post #15

Earlier quoted context omitted.

Its 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."

> 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 count…

With all due respect, why do you believe that your experience is meaningful compared to people writing actual industrial-scale runtimes, when RC is the hello world of GC algorithms? It’s not that they don’t know about it, it’s that they studied the topic countless times and found significant difference between the two approaches, to the point that no language considered fast uses RC (or if they do, they do so because it is a useful escape hatch to manual memory management that doesn’t need support from the runtime).

Re: Reference count, don't garbage collect

#47
post #43

Earlier quoted context omitted.

The 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 dec…

> 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 and Arc 2. C++, in std::shared_ptr 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 al…

> Deallocating can take arbitrarily long, but deallocation does not stop-the-world

And modern GCs only have to stop the current thread to check for which thread-local objects are alive. The alice ones are moved to another generation, making the space reusable for free.

And atomic writes need synchronization which is crazy expensive, I honestly don’t get why you think it isn’t.

Also, just try writing rust/c++ code that relies entirely on RC vs Java in an object heavy workload - I really don’t think it is an open question in any shape or form.

Re: Reference count, don't garbage collect

#48
post #27
post #25

Reference counting can also have unpredictable hits if you release any large data structures. Whoever drops the last reference suddenly gets to sit through the entire deep set of items to release ( unless you can hand off the release cascade to a background thread ). I've never heard of a reference counting implementation that can handle memory compaction. Every time you update a reference count, which is every time…

> hits if you release any large data structures. Well, that depends in how is the RC done. This is key to understand because if you can control it, the RC become cheaper. You can see this way on http://sblom.github.io/openj-core/iojNoun.htm ie: If instead of `[Rc(1), Rc(2)]` you do `Rc([1, 2])` that work great.

How is that not the exact same for tracing GC?

Re: Reference count, don't garbage collect

#49
post #24

Boy, I can't wait for theangeryemacsshibe (posts here as hayley-patton) to tear into this one. But yeah, the correct way to handle resources (not just memory!) is with value semantics and RAII. Because then you know the object will be cleaned up as soon as it goes out of scope with zero additional effort on your part. In places where this is not appropriate, a simple reference counting scheme may be used, but the ide…

Why not just write embedded programs with fixed size memory allocation then if we are that okay with restricting the programs we write?

Re: Reference count, don't garbage collect

#50
post #47
post #43

Earlier quoted context omitted.

> 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 and Arc 2. C++, in std::shared_ptr 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 al…

> Deallocating can take arbitrarily long, but deallocation does not stop-the-world And modern GCs only have to stop the current thread to check for which thread-local objects are alive. The alice ones are moved to another generation, making the space reusable for free . And atomic writes need synchronization which is crazy expensive, I honestly don’t get why you think it isn’t. Also, just try writing rust/c++ code th…

> The alice ones are moved to another generation, making the space reusable for free.

It's pretty hilarious to me that you first say "they have to move it to another generation" and then you say "it's free!" It's like saying "I paid for my dinner, and now I get to eat it for free!"

Also: what do you think `free()` does? All modern memory allocators do this trick, keeping thread-local caches. This is not an advantage of GCs when reference counting does it as well.

Almost all modern GCs are stop-the-world in at least phases, and for good reason: stop-the-world GCs are higher performance. You pay in other ways for skipping that stop.

> And atomic writes need synchronization which is crazy expensive, I honestly don’t get why you think it isn’t.

Because I've actually benchmarked it: https://quick-bench.com/q/ISEetAHOohv-GaEuYR-7MajJgTc

18.5 nanoseconds fits under no reasonable definition of "crazy expensive", not when a regular increment clocks in at 5.9 nanoseconds. And there is extremely few situations where you increment a reference count more than, like, 5 times. It's just not an issue.

This is like cargo cult programming: you've been told these things and never tested them in the real world, and you have all these preconceived notions that just don't stand up to two minutes of scrutiny.

> Also, just try writing rust/c++ code that relies entirely on RC vs Java in an object heavy workload - I really don’t think it is an open question in any shape or form.

Yes, of course garbage collectors are easier to use than reference counting. Nobody has ever disputed this. That is the whole raison d'etre of garbage collectors. This is not what the discussion is about, it's about performance.

I'm done with this thread now, unless anybody can show me any actual data to back anything you say up. It's really tiring. I started this by saying "I don't actually know", and everyone replying to me has been so darn certain of everything they say while being outright incorrect about most things, and without any actual data to back up the rest.

Post reply on HN