
Introducing Riptide: WebKit's Retreating Wavefront Concurrent Garbage Collector - pizlonator
https://webkit.org/blog/7122/introducing-riptide-webkits-retreating-wavefront-concurrent-garbage-collector/
======
bdash
I'm impressed by Filip's knack for explaining complex topics in an
approachable manner. His previous post on WebKit's locking infrastructure[1]
is a similarly good read.

A few amusing parts of this post that stood out to me:

* A single sentence containing half a dozen links to Filip's previous work on garbage collection.

* "See here for the proof", linking to a 200+ line comment in WebKit's source.

* "But since this is JavaScript, we get to have a lot more fun".

[1]: [https://webkit.org/blog/6161/locking-in-
webkit/](https://webkit.org/blog/6161/locking-in-webkit/)

~~~
agentultra
While that proof is amusing I was hoping for... an actual proof that I can
give to a verifier. Ah well. A hundred or so lines of comments is probably
right.

Still this looks to be some impressive work and I did enjoy this post.

~~~
pizlonator
I'm a mathematician. Proofs are one of the means of communication between me
and other mathematicians.

I don't give a crap if a computer can read my proof.

~~~
corndoge
Well, us programmers do since computers are the ones we're communicating with.

~~~
pizlonator
I like to write programs when I program and proofs when I mathematize. The
program is for the computer and the proof is for a human.

I think you're getting it all mixed up!

~~~
corndoge
I think you're wrong!

------
mwcampbell
This also applies to JavaScriptCore when used outside of WebKit, right? If so,
then it would also benefit React Native, among other things.

~~~
scarlac
If Node (React Native) uses V8, Would it not have to be ported to V8 first
(ie. Chromium needs to re-implement the technique)?

~~~
pizlonator
Sure, but JavaScriptCore has its own API for native apps that want to
JavaScript. That's what react native does.

------
ahh

      Some accounting only needs to happen when visiting the
      object for the first time. The complete barrier is simply:
    
         object->field = newValue;
         if (object->cellState == Old)
           remember(object);
    

This surely elides some synchronization? One would think that either this has
to communicate with the collector (to prevent a racing concurrent collection
from missing the referent), or happen in the opposite order.

Or perhaps it is relying on the calling mutator thread necessarily holding a
reference to the young object in question? Even then there is some non-trivial
ordering work to be done here (in particular on weakly ordered architectures.)

I'm sure this is well handled but as this is one of the main difficulties in
implementing good write barriers I am curious what they did.

~~~
pizlonator
The barrier code you quote is for our old generational GC. Keep reading to see
what the riptide barrier looks like. See the section on retreating wavefront.

~~~
ahh
Aha, thanks; I didn't realize the first generational section was for an older
version.

However, the riptide barrier description still isn't 100% clear how it avoids
terminating a sweep (I believe "drain" in your termination) in the middle of a
barrier execution; in particular I'm not concerned about the data race between
the field write and the mark bit, but the GC reading mark bits, believing the
drain has terminated, and freeing memory while a mutator thread has written a
field but not written the mark bit.

Are you just relying on the stop-the-world behavior relative to roots that you
mention briefly?

~~~
pizlonator
The collector can only flip at safepoints. Safepoints cannot happen inside the
barrier or in between the store the barrier protects and the barrier itself.

Flip = term of art for when the GC terminates and decides which objects are
free.

------
ksec
Sometimes I still wish Apple release Safari for Windows.

On another note, Webkit seems to be following a much more open manner, and has
shift to working more towards Web App optimization rather then Web Pages.

------
geodel
Hope some knowledgeable people can compare/contrast this with Blink/Servo/Edge
etc approach.

~~~
the8472
It's a non-compacting collector.

In principle the downsides compared to compacting collectors is reduced cache
locality, fragmentation, somewhat higher allocation overhead and higher memory
footprint.

On the other hand it is a lot easier to implement concurrent collectors when
objects are not moved.

 _> The changes that transformed WebKit’s collector were landed over the past
six months, starting with the painful work of removing WebKit’s previous use
of copying._

It is curious that mozilla chose the opposite, doing the painful work of
turning a conservative, non-moving collector into a precise, compacting one.

~~~
BenoitP
> painful work of removing WebKit’s previous use of copying

Do I understand correctly that the hard work was done, and this is a
regression?

~~~
the8472
I understood it as that their collector used to be precise, moving and they
turned it into a partially-conservative, non-moving one.

~~~
pizlonator
Clarification: our previous collector did conservative root scanning, used a
less evolved mark-sweep system for some objects, and used a Bartlett-style of
quick-release compaction for other objects.

Riptide removes the copying because concurrent or event incremental copying is
sure to introduce overhead. Are a minimum you either need a barrier on all
pointer reads from the heap or all writes to the heap, and none of those
barriers resemble our old generational barrier so this would be new overhead.
Riptide only barriers writes of pointers to the heap, and uses a single
unified barrier for both concurrency and generations to get maximum
throughput. Riptide's concurrency actually improves end-to-end execution time
because the overhead of the concurrency is negligible so you just get to enjoy
the benefit of continuing to run while the GC runs, so you run faster overall.
The speed-up was like 5% on the splay test, and I measured speed-ups in other
things I cared about, like page load times.

~~~
olliej
It still does conservative root scanning for the stack. Hurrah for ABI
compatibility!

------
hacker_9
tl;dr:

 _" Riptide is an improvement to WebKit’s collector and retains most of the
things that made the old algorithm great. The changes that transformed
WebKit’s collector were landed over the past six months, starting with the
painful work of removing WebKit’s previous use of copying. Riptide combines
Guy Steele’s classic retreating wavefront write barrier with a mature sticky-
mark-sweep collector and lots of concurrency tricks to get a useful
combination of high GC throughput and low GC latency."_

------
stcredzero
The post is written/formatted in a confusing way for those not already versed
in garbage collection. In particular, the important but idiosyncratic term
"draining" is not given a reference link, but is instead given a different
font that makes it fade into the background.

~~~
pizlonator
"Most of the time in GC is spent performing a depth-first search over marked
objects that we call _draining_."

It's a term we use in our code to refer to that depth-first search.

