Commit 696eeb39 authored by Etienne Pierre-doray's avatar Etienne Pierre-doray Committed by Commit Bot

[Heap]: Young generation marking uses Jobs.

Replaces ItemParallelJob by std::vector to hold marking items.
IndexGenerator is used to iterate over evacuation items.
slots_ is moved from items to YoungGenerationMarkingTask to reduce
synchronisation.

Change-Id: Iac7aba215e8ba545c12a9ab6c810d343234fbbbf
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2440830
Commit-Queue: Etienne Pierre-Doray <etiennep@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70575}
parent e6b2d673
......@@ -4339,18 +4339,6 @@ MinorMarkCompactCollector::~MinorMarkCompactCollector() {
delete main_marking_visitor_;
}
int MinorMarkCompactCollector::NumberOfParallelMarkingTasks(int pages) {
DCHECK_GT(pages, 0);
if (!FLAG_minor_mc_parallel_marking) return 1;
// Pages are not private to markers but we can still use them to estimate the
// amount of marking that is required.
const int kPagesPerTask = 2;
const int wanted_tasks = Max(1, pages / kPagesPerTask);
return Min(NumberOfAvailableCores(),
Min(wanted_tasks,
MinorMarkCompactCollector::MarkingWorklist::kMaxNumTasks));
}
void MinorMarkCompactCollector::CleanupSweepToIteratePages() {
for (Page* p : sweep_to_iterate_pages_) {
if (p->IsFlagSet(Page::SWEEP_TO_ITERATE)) {
......@@ -4733,43 +4721,25 @@ MinorMarkCompactCollector::CreateRememberedSetUpdatingItem(
heap(), non_atomic_marking_state(), chunk, updating_mode);
}
class MarkingItem;
class PageMarkingItem;
class RootMarkingItem;
class YoungGenerationMarkingTask;
class MarkingItem : public ItemParallelJob::Item {
public:
~MarkingItem() override = default;
virtual void Process(YoungGenerationMarkingTask* task) = 0;
};
class YoungGenerationMarkingTask : public ItemParallelJob::Task {
class YoungGenerationMarkingTask {
public:
YoungGenerationMarkingTask(
Isolate* isolate, MinorMarkCompactCollector* collector,
MinorMarkCompactCollector::MarkingWorklist* global_worklist, int task_id)
: ItemParallelJob::Task(isolate),
collector_(collector),
marking_worklist_(global_worklist, task_id),
: marking_worklist_(global_worklist, task_id),
marking_state_(collector->marking_state()),
visitor_(marking_state_, global_worklist, task_id) {
local_live_bytes_.reserve(isolate->heap()->new_space()->Capacity() /
Page::kPageSize);
}
void RunInParallel(Runner runner) override {
if (runner == Runner::kForeground) {
TRACE_GC(collector_->heap()->tracer(),
GCTracer::Scope::MINOR_MC_MARK_PARALLEL);
ProcessItems();
} else {
TRACE_BACKGROUND_GC(
collector_->heap()->tracer(),
GCTracer::BackgroundScope::MINOR_MC_BACKGROUND_MARKING);
ProcessItems();
}
}
int slots() const { return slots_; }
void IncrementSlots() { ++slots_; }
void MarkObject(Object object) {
if (!Heap::InYoungGeneration(object)) return;
......@@ -4780,34 +4750,6 @@ class YoungGenerationMarkingTask : public ItemParallelJob::Task {
}
}
private:
void ProcessItems() {
double marking_time = 0.0;
{
TimedScope scope(&marking_time);
MarkingItem* item = nullptr;
while ((item = GetItem<MarkingItem>()) != nullptr) {
item->Process(this);
item->MarkFinished();
EmptyLocalMarkingWorklist();
}
EmptyMarkingWorklist();
DCHECK(marking_worklist_.IsLocalEmpty());
FlushLiveBytes();
}
if (FLAG_trace_minor_mc_parallel_marking) {
PrintIsolate(collector_->isolate(), "marking[%p]: time=%f\n",
static_cast<void*>(this), marking_time);
}
}
void EmptyLocalMarkingWorklist() {
HeapObject object;
while (marking_worklist_.Pop(&object)) {
const int size = visitor_.Visit(object);
IncrementLiveBytes(object, size);
}
}
void EmptyMarkingWorklist() {
HeapObject object;
while (marking_worklist_.Pop(&object)) {
......@@ -4826,20 +4768,20 @@ class YoungGenerationMarkingTask : public ItemParallelJob::Task {
}
}
MinorMarkCompactCollector* collector_;
private:
MinorMarkCompactCollector::MarkingWorklist::View marking_worklist_;
MinorMarkCompactCollector::MarkingState* marking_state_;
YoungGenerationMarkingVisitor visitor_;
std::unordered_map<Page*, intptr_t, Page::Hasher> local_live_bytes_;
int slots_ = 0;
};
class PageMarkingItem : public MarkingItem {
class PageMarkingItem : public ParallelWorkItem {
public:
explicit PageMarkingItem(MemoryChunk* chunk, std::atomic<int>* global_slots)
: chunk_(chunk), global_slots_(global_slots), slots_(0) {}
~PageMarkingItem() override { *global_slots_ = *global_slots_ + slots_; }
explicit PageMarkingItem(MemoryChunk* chunk) : chunk_(chunk) {}
~PageMarkingItem() = default;
void Process(YoungGenerationMarkingTask* task) override {
void Process(YoungGenerationMarkingTask* task) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.gc"),
"PageMarkingItem::Process");
base::MutexGuard guard(chunk_->mutex());
......@@ -4896,23 +4838,102 @@ class PageMarkingItem : public MarkingItem {
USE(success);
DCHECK(success);
task->MarkObject(heap_object);
slots_++;
task->IncrementSlots();
return KEEP_SLOT;
}
return REMOVE_SLOT;
}
MemoryChunk* chunk_;
std::atomic<int>* global_slots_;
int slots_;
};
class YoungGenerationMarkingJob : public v8::JobTask {
public:
YoungGenerationMarkingJob(
Isolate* isolate, MinorMarkCompactCollector* collector,
MinorMarkCompactCollector::MarkingWorklist* global_worklist,
std::vector<PageMarkingItem> marking_items, std::atomic<int>* slots)
: isolate_(isolate),
collector_(collector),
global_worklist_(global_worklist),
marking_items_(std::move(marking_items)),
remaining_marking_items_(marking_items_.size()),
generator_(marking_items_.size()),
slots_(slots) {}
void Run(JobDelegate* delegate) override {
if (delegate->IsJoiningThread()) {
TRACE_GC(collector_->heap()->tracer(),
GCTracer::Scope::MINOR_MC_MARK_PARALLEL);
ProcessItems(delegate);
} else {
TRACE_BACKGROUND_GC(
collector_->heap()->tracer(),
GCTracer::BackgroundScope::MINOR_MC_BACKGROUND_MARKING);
ProcessItems(delegate);
}
}
size_t GetMaxConcurrency(size_t worker_count) const override {
// Pages are not private to markers but we can still use them to estimate
// the amount of marking that is required.
const int kPagesPerTask = 2;
size_t items = remaining_marking_items_.load(std::memory_order_relaxed);
size_t num_tasks = std::max((items + 1) / kPagesPerTask,
global_worklist_->GlobalPoolSize());
return std::min<size_t>(
num_tasks, MinorMarkCompactCollector::MarkingWorklist::kMaxNumTasks);
}
private:
void ProcessItems(JobDelegate* delegate) {
double marking_time = 0.0;
{
TimedScope scope(&marking_time);
YoungGenerationMarkingTask task(isolate_, collector_, global_worklist_,
delegate->GetTaskId());
ProcessMarkingItems(&task);
task.EmptyMarkingWorklist();
task.FlushLiveBytes();
*slots_ += task.slots();
}
if (FLAG_trace_minor_mc_parallel_marking) {
PrintIsolate(collector_->isolate(), "marking[%p]: time=%f\n",
static_cast<void*>(this), marking_time);
}
}
void ProcessMarkingItems(YoungGenerationMarkingTask* task) {
while (remaining_marking_items_.load(std::memory_order_relaxed) > 0) {
base::Optional<size_t> index = generator_.GetNext();
if (!index) return;
for (size_t i = *index; i < marking_items_.size(); ++i) {
auto& work_item = marking_items_[i];
if (!work_item.TryAcquire()) break;
work_item.Process(task);
task->EmptyMarkingWorklist();
if (remaining_marking_items_.fetch_sub(1, std::memory_order_relaxed) <=
1) {
return;
}
}
}
}
Isolate* isolate_;
MinorMarkCompactCollector* collector_;
MinorMarkCompactCollector::MarkingWorklist* global_worklist_;
std::vector<PageMarkingItem> marking_items_;
std::atomic_size_t remaining_marking_items_{0};
IndexGenerator generator_;
std::atomic<int>* slots_;
};
void MinorMarkCompactCollector::MarkRootSetInParallel(
RootMarkingVisitor* root_visitor) {
std::atomic<int> slots;
{
ItemParallelJob job(isolate()->cancelable_task_manager(),
&page_parallel_job_semaphore_);
std::vector<PageMarkingItem> marking_items;
// Seed the root set (roots + old->new set).
{
......@@ -4930,22 +4951,25 @@ void MinorMarkCompactCollector::MarkRootSetInParallel(
root_visitor);
// Create items for each page.
RememberedSet<OLD_TO_NEW>::IterateMemoryChunks(
heap(), [&job, &slots](MemoryChunk* chunk) {
job.AddItem(new PageMarkingItem(chunk, &slots));
heap(), [&marking_items](MemoryChunk* chunk) {
marking_items.emplace_back(chunk);
});
}
// Add tasks and run in parallel.
{
// The main thread might hold local items, while GlobalPoolSize() == 0.
// Flush to ensure these items are visible globally and picked up by the
// job.
worklist()->FlushToGlobal(kMainThreadTask);
TRACE_GC(heap()->tracer(), GCTracer::Scope::MINOR_MC_MARK_ROOTS);
const int new_space_pages =
static_cast<int>(heap()->new_space()->Capacity()) / Page::kPageSize;
const int num_tasks = NumberOfParallelMarkingTasks(new_space_pages);
for (int i = 0; i < num_tasks; i++) {
job.AddTask(
new YoungGenerationMarkingTask(isolate(), this, worklist(), i));
}
job.Run();
V8::GetCurrentPlatform()
->PostJob(v8::TaskPriority::kUserBlocking,
std::make_unique<YoungGenerationMarkingJob>(
isolate(), this, worklist(), std::move(marking_items),
&slots))
->Join();
DCHECK(worklist()->IsEmpty());
}
}
......
......@@ -858,8 +858,6 @@ class MinorMarkCompactCollector final : public MarkCompactCollectorBase {
std::unique_ptr<UpdatingItem> CreateRememberedSetUpdatingItem(
MemoryChunk* chunk, RememberedSetUpdatingMode updating_mode) override;
int NumberOfParallelMarkingTasks(int pages);
void SweepArrayBufferExtensions();
MarkingWorklist* worklist_;
......@@ -873,6 +871,7 @@ class MinorMarkCompactCollector final : public MarkCompactCollectorBase {
NonAtomicMarkingState non_atomic_marking_state_;
friend class YoungGenerationMarkingTask;
friend class YoungGenerationMarkingJob;
friend class YoungGenerationMarkingVisitor;
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment