RFR(S): 8005032: G1: Cleanup serial reference processing closures in concurrent marking
John Cuthbertson
john.cuthbertson at oracle.com
Thu Jan 10 23:23:52 UTC 2013
Hi Bengt,
Thanks for looking over the code changes. Be prepared for some gory
details. :) Replies inline...
On 1/9/2013 7:35 AM, Bengt Rutisson wrote:
>
> In ConcurrentMark::weakRefsWork() there is this code:
>
> 2422 if (rp->processing_is_mt()) {
> 2423 // Set the degree of MT here. If the discovery is done MT,
> there
> 2424 // may have been a different number of threads doing the
> discovery
> 2425 // and a different number of discovered lists may have Ref
> objects.
> 2426 // That is OK as long as the Reference lists are balanced (see
> 2427 // balance_all_queues() and balance_queues()).
> 2428 rp->set_active_mt_degree(active_workers);
> 2429 }
>
> Could we now always call rp->set_active_mt_degree() ? Maybe I am
> missing the point here, but I thought that we are now using queues and
> all rp->set_active_mt_degree() does is set the number of queues to
> active_workers. Which will be 1 for the single threaded mode.
Yes - most likely we can but I would prefer to set active_workers using:
// We need at least one active thread. If reference processing is
// not multi-threaded we use the current (ConcurrentMarkThread) thread,
// otherwise we use the work gang from the G1CollectedHeap and we
// utilize all the worker threads we can.
uint active_workers = (rp->processing_is_mt() && g1h->workers() != NULL
? g1h->workers()->active_workers()
: 1U);
since single threaded versus multi-threaded reference processing is
determined using the ParallelRefProcEnabled flag. The number of active
workers here is the number of workers in G1's STW work gang - which is
controlled via ParallelGCThreads.
>
> If we do that we can also remove the first part of the assert a bit
> further down:
>
> 2449 assert(!rp->processing_is_mt() || rp->num_q() ==
> active_workers, "why not");
OK. Done.
>
> Also, I think I would like to move the code you added to
> G1CMParDrainMarkingStackClosure::do_void() into
> ConcurrentMark::weakRefsWork() somewhere. Maybe something like:
>
> if (!rp->processing_is_mt()) {
> set_phase(1, false /* concurrent */);
> }
>
> It is a bit strange to me that G1CMParDrainMarkingStackClosure should
> set this up. If we really want to keep it in
> G1CMParDrainMarkingStackClosure I think the constructor would be a
> better place to do it than do_void().
Setting it once in weakRefsWork() will not be sufficient. We will run
into an assertion failure in ParallelTaskTerminator::offer_termination().
During the reference processing, the do_void() method of the complete_gc
oop closure (in our case the complete gc oop closure is an instance of
G1CMParDrainMarkingStackClosure) is called multiple times (in
process_phase1, sometimes process_phase2, process_phase3, and
process_phaseJNI)
Setting the phase sets the number of active tasks (or threads) that the
termination protocol in do_marking_step() will wait for. When an
invocation of do_marking_step() offers termination, the number of
tasks/threads in the terminator instance is decremented. So Setting the
phase once will let the first execution of do_marking_step (with
termination) from process_phase1() succeed, but subsequent calls to
do_marking_step() will result in the assertion failure.
We also can't unconditionally set it in the do_void() method or even the
constructor of G1CMParDrainMarkingStackClosure. Separate instances of
this closure are created by each of the worker threads in the MT-case.
Note when processing is multi-threaded the complete_gc instance used is
the one passed into the ProcessTask's work method (passed into
process_discovered_references() using the task executor instance) which
may not necessarily be the same complete gc instance as the one passed
directly into process_discovered_references().
It might be possible to record whether processing is MT in the
G1CMRefProcTaskExecutor class and always pass the executor instance into
process_discovered_references. We could then set processing to MT so
that the execute() methods in the executor instance are invoked but call
the Proxy class' work method directly. Then we could override the
set_single_threaded() routine (called just before process_phaseJNI) to
set the phase.
I don't think that would be any clearer.
Perhaps a better name for the _is_par flag would be _processing_is_mt
(and set it using ReferenceProcessor::processing_is_mt())?
> Some minor comments:
>
> In G1CMParKeepAliveAndDrainClosure and G1CMParDrainMarkingStackClosure
> constructors there is this assert:
>
> assert(_task->worker_id() == 0 || _is_par, "sanity");
>
> I think it is good, but I had to think a bit about what it meant and
> to me I think it would be quicker to understand if it was the other
> way around:
>
> assert(_is_par || _task->worker_id() == 0, "Only worker 0 should be
> used if single threaded");
Sure no problem. Done.
> Remove newline on line 2254 ?
Sure. What about the blank lines on 2187 and 2302 for consistency?
> ConcurrentMark::weakRefsWork()
>
> How about introducing a variable that either hold the value of
> &par_task_executor or NULL depending on rp->processing_is_mt()? That
> way we don't have to duplicate and inline this test twice:
>
> (rp->processing_is_mt() ? &par_task_executor : NULL)
OK.
> As a separate change it might be worth renaming the closures to not
> have "Par" in the name, now that they are not always parallel...
>
> G1CMParKeepAliveAndDrainClosure -> G1CMKeepAliveAndDrainClosure
> G1CMParDrainMarkingStackClosure -> G1CMDrainMarkingStackClosure
>
> But it would be very confusing to do this in the same change.
I think we can change the names. The change shouldn't be that confusing.
Thanks. A new webrev will appear shortly.
JohnC
More information about the hotspot-gc-dev
mailing list