Closed Bug 1357196 Opened 7 years ago Closed 7 years ago

Do not synchronously wait for background sweep thread during GC

Categories

(Core :: JavaScript: GC, enhancement)

enhancement
Not set
normal

Tracking

()

RESOLVED WORKSFORME
Performance Impact high

People

(Reporter: djvj, Unassigned)

References

(Blocks 1 open bug)

Details

Noticed this while looking at gc timing console outputs.  In some cases, we synchronously wait for a background sweep thread to complete (without a timeout) before proceeding with GC.

This wait should be capped by a timeout (or not wait at all for the background thread to complete).
Can you point me at where this is happening?  There are a bunch of things that happen in the background but we shouldn't be blocking on them during an incremental GC.
Relevant GC Timings log here: https://pastebin.mozilla.org/9019450
We discussed this at our GC meeting today and concluded that this is a case where the incremental GC got turned into a nonincremental gotta-finish-NOW GC, most likely because it exceeded the allocation threshold. We also talked about how it would probably be a good idea to drop the lower threshold so they're not so close together, to reduce the frequency that this will happen.
Whiteboard: [qf]
(In reply to Steve Fink [:sfink] [:s:] from comment #3)
I was thinking we could try raising the upper threshold considerably.  We'd have to do something to curtail incremental GCs that are not making progress.
Couple questions.  I assume that nursery GCs can run in-between incremental GCs.  Is that true?  Presumably if a nursery GC runs in between incremental slices, the newly tenured objects are added to the write-barrier log?

Given that, would a reasonable measure of "are we making progress" be:

A = number of objects marked by the last incremental slice.
B = number of objects added to write barrier log since last incremental slice finished.
C = number of objects on heap

D = A - B = absolute amount of work done by incremental GC slice that was not subsequently "clawed back" by the mutator
E = D / C = normalized progress of last incremental slice.

We keep a window of the last 5 (or some other number) of Es, and take their sum as an indication of how much we're falling behind?
(In reply to Kannan Vijayan [:djvj] from comment #5)
> Couple questions.  I assume that nursery GCs can run in-between incremental
> GCs.  Is that true?

yes

> Presumably if a nursery GC runs in between incremental
> slices, the newly tenured objects are added to the write-barrier log?

I'm not sure what you mean by "write-barrier log". That sounds like the store buffer, which is for generational, and it only tracks edges between the tenured heap and the nursery, and is thus empty after a nursery GC. The write barrier for incremental GC is really a "delete barrier", for ensuring that no object gets moved to behind the incremental frontier. It does not use a log, it just marks anything deleted. Unless you mean "push onto mark stack" == "append to write log"? That would make sense, except that I'd think that would be *forward* progress, not backwards progress, in terms of marking the whole heap.

Unless you're talking about the problem where you're retaining trash. If a pre-write (delete) barrier marks something that would otherwise be garbage, then I could see considering it to be negative progress -- or rather, forward progress, but the size of the heap increases (possibly faster, if the barriered object is holding onto a large graph.)

> Given that, would a reasonable measure of "are we making progress" be:
> 
> A = number of objects marked by the last incremental slice.
> B = number of objects added to write barrier log since last incremental
> slice finished.
> C = number of objects on heap

I'm defining progress as "getting closer to marking the portion of the heap that by the end of the incremental GC, will be considered to be live." (Even if some of the stuff "considered to be live" is only considered to be live because of the conservative pre-write barrier marking necessary for implementing snapshot-at-the-beginning.)

What I think of in terms of an iGC slice making progress is all about per-slice overhead. If we spend our entire slice scanning through the roots, for example, then we make no forward progress in marking the heap. Allocation rate during mutation is not really a problem, since we allocate black if iGC is in progress.

So I guess the per-slice overhead is all the stuff in GCCycle before and after marking - <http://searchfox.org/mozilla-central/source/js/src/jsgc.cpp#6432>. I see:

 - EvictAllNurseries
 - cancel and wait for the background alloc thread
 - AutoGCRooter::traceAllWrappers for all cooperating contexts
 - resetting some stats on zones

That really doesn't look like much. So I'm not sure if per-slice overhead is a big deal anymore.

The per-slice progress is number of objects marked.

> D = A - B = absolute amount of work done by incremental GC slice that was
> not subsequently "clawed back" by the mutator

The mutator can't really claw back progress with snapshot at the beginning.

> E = D / C = normalized progress of last incremental slice.
> 
> We keep a window of the last 5 (or some other number) of Es, and take their
> sum as an indication of how much we're falling behind?

So I don't think it's a matter of falling behind in terms of amount of work done. Instead, it's whether we can finish the iGC before we hit something that makes us decide to finish up the GC in one slice -- hitting an allocation threshold, or CC_WAITING, or...?
Oops, I think there might be a better bug for this discussion.
Whiteboard: [qf] → [qf:p1]
As per comment 3 this is really a scheduling issue rather than a blocking waiting for other threads issue.
Status: NEW → RESOLVED
Closed: 7 years ago
Resolution: --- → WORKSFORME
Performance Impact: --- → P1
Whiteboard: [qf:p1]
You need to log in before you can comment on or make changes to this bug.