Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8265842: G1: Introduce API to run multiple separate tasks in a single gangtask #3653

112 changes: 112 additions & 0 deletions src/hotspot/share/gc/g1/g1BatchedGangTask.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/

#include "precompiled.hpp"

#include "gc/g1/g1BatchedGangTask.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1GCParPhaseTimesTracker.hpp"
#include "runtime/atomic.hpp"
#include "utilities/growableArray.hpp"

void G1AbstractSubTask::record_work_item(uint worker_id, uint index, size_t count) {
G1CollectedHeap* g1h = G1CollectedHeap::heap();
g1h->phase_times()->record_thread_work_item(_tag, worker_id, count, index);
}

const char* G1AbstractSubTask::name() const {
G1CollectedHeap* g1h = G1CollectedHeap::heap();
return g1h->phase_times()->phase_name(_tag);
}

bool G1BatchedGangTask::try_claim_serial_task(int& task) {
task = Atomic::load(&_num_serial_tasks_done);
if (task < _serial_tasks.length()) {
task = Atomic::fetch_and_add(&_num_serial_tasks_done, 1);
}
return task < _serial_tasks.length();
}

void G1BatchedGangTask::add_serial_task(G1AbstractSubTask* task) {
assert(task != nullptr, "must be");
_serial_tasks.push(task);
}

void G1BatchedGangTask::add_parallel_task(G1AbstractSubTask* task) {
assert(task != nullptr, "must be");
_parallel_tasks.push(task);
}

G1BatchedGangTask::G1BatchedGangTask(const char* name, G1GCPhaseTimes* phase_times) :
AbstractGangTask(name),
_num_serial_tasks_done(0),
_phase_times(phase_times),
_serial_tasks(),
_parallel_tasks() {
}

uint G1BatchedGangTask::num_busy_workers() const {
double sum = 0.0;
for (G1AbstractSubTask* task : _serial_tasks) {
sum += task->num_busy_workers();
}
for (G1AbstractSubTask* task : _parallel_tasks) {
sum += task->num_busy_workers();
}
return ceil(sum);
}

void G1BatchedGangTask::set_max_workers(uint max_workers) {
for (G1AbstractSubTask* task : _serial_tasks) {
task->set_max_workers(max_workers);
}
for (G1AbstractSubTask* task : _parallel_tasks) {
task->set_max_workers(max_workers);
}
}

void G1BatchedGangTask::work(uint worker_id) {
int t = 0;
while (try_claim_serial_task(t)) {
G1AbstractSubTask* task = _serial_tasks.at(t);
G1GCParPhaseTimesTracker x(_phase_times, task->tag(), worker_id);
task->do_work(worker_id);
}
for (G1AbstractSubTask* task : _parallel_tasks) {
G1GCParPhaseTimesTracker x(_phase_times, task->tag(), worker_id);
task->do_work(worker_id);
}
}

G1BatchedGangTask::~G1BatchedGangTask() {
assert(Atomic::load(&_num_serial_tasks_done) >= _serial_tasks.length(),
"Only %d tasks of %d claimed", Atomic::load(&_num_serial_tasks_done), _serial_tasks.length());

for (G1AbstractSubTask* task : _parallel_tasks) {
delete task;
}
for (G1AbstractSubTask* task : _serial_tasks) {
delete task;
}
}
141 changes: 141 additions & 0 deletions src/hotspot/share/gc/g1/g1BatchedGangTask.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/

#ifndef SHARE_GC_G1_G1BATCHEDGANGTASK_HPP
#define SHARE_GC_G1_G1BATCHEDGANGTASK_HPP

#include "gc/g1/g1GCPhaseTimes.hpp"
#include "gc/shared/workgroup.hpp"
#include "memory/allocation.hpp"

template <typename E, MEMFLAGS F>
class GrowableArrayCHeap;

// G1AbstractSubTask represents a task to be performed either within an
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

within an -> within a

// G1BatchedGangTask running on a single worker ("serially") or multiple workers
// ("in parallel"). A G1AbstractSubTask is always associated with a phase tag
// that is used to automatically store timing information.
//
// A "serial" task is some piece of work that either can not be parallelized
// easily, or is typically so short that parallelization is not worth the effort.
// Current examples would be summarizing per worker thread information gathered
// during garbage collection (e.g. Merge PSS work).
//
// A "parallel" task could be some large amount of work that typically naturally
// splits across the heap in some way. Current examples would be clearing the
// card table.
//
// See G1BatchedGangTask for information on execution.
class G1AbstractSubTask : public CHeapObj<mtGC> {
G1GCPhaseTimes::GCParPhases _tag;

NONCOPYABLE(G1AbstractSubTask);

protected:
// Record work item for this tag in G1GCPhaseTimes.
void record_work_item(uint worker_id, uint index, size_t count);

public:
G1AbstractSubTask(G1GCPhaseTimes::GCParPhases tag) : _tag(tag) { }
virtual ~G1AbstractSubTask() { }

// How many workers (threads) would this task be able to keep busy for at least
// as long as to amortize worker startup costs.
// Called by G1BatchedGangTask to determine total number of workers.
virtual double num_busy_workers() const { return 1.0; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe make pure virtual?


// Called by G1BatchedGangTask to provide information about the the maximum
// number of workers for all subtasks after it has been determined.
virtual void set_max_workers(uint max_workers) { }

// Perform the actual work. Gets the worker id it is run on passed in.
virtual void do_work(uint worker_id) = 0;

// Tag for this G1AbstractSubTask.
G1GCPhaseTimes::GCParPhases tag() const { return _tag; }
// Human readable name derived from the tag.
const char* name() const;
};

// G1BatchedGangTask runs a set of G1AbstractSubTask using a work gang.
//
// Subclasses of this class add their G1AbstractSubTasks into either the list
// of "serial" or the list of "parallel" tasks.
// During execution in the work gang, this class will make sure that the "serial"
// tasks are executed by a single worker only exactly once, but different "serial"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

worker only exactly once -> worker exactly once

// tasks may be executed in parallel using different workers. "Parallel" tasks'
// do_work() method may be called by different workers passing a different
// worker_id at the same time, but at most once per given worker id.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

worker id -> worker_id (I hope to one day rename worker_id to work_id, work_unit, or something else, but if we are not referring to the variable, we should not describe it as a worker id, as it does not describe a worker but a unit of work.

//
// The G1AbstractSubTask's do_work() method gets a unique worker_id each time the
// method is called.
// There is no guarantee that G1AbstractSubTasks::do_work() of different tasks
// are actually run in parallel.
//
// The current implementation assumes that constructors and destructors of the
// G1AbstractSubTasks can executed in the constructor/destructor of an instance
// of this class.
//
// The constructor, destructor and the do_work() methods from different
// G1AbstractSubTasks may run in any order so they must not have any
// dependencies at all.
//
// For a given G1AbstractSubTask T call order of its methods are as follows:
//
// 1) T()
// 2) T::thread_usage()
// 3) T::set_max_workers()
// 4) T::do_work() // potentially in parallel with any other registered G1AbstractSubTask
// 5) ~T()
//
class G1BatchedGangTask : public AbstractGangTask {
volatile int _num_serial_tasks_done;
G1GCPhaseTimes* _phase_times;

bool try_claim_serial_task(int& task);

NONCOPYABLE(G1BatchedGangTask);

GrowableArrayCHeap<G1AbstractSubTask*, mtGC> _serial_tasks;
GrowableArrayCHeap<G1AbstractSubTask*, mtGC> _parallel_tasks;

protected:
void add_serial_task(G1AbstractSubTask* task);
void add_parallel_task(G1AbstractSubTask* task);

G1BatchedGangTask(const char* name, G1GCPhaseTimes* phase_times);

public:
void work(uint worker_id) override;

// How many workers can this gang task keep busy and could be started.
uint num_busy_workers() const;
// Informs the G1AbstractSubTasks about that we will start execution with the
// given number of workers.
void set_max_workers(uint max_workers);

~G1BatchedGangTask();
};

#endif // SHARE_GC_G1_G1BATCHEDGANGTASK_HPP
Loading