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() { ...@@ -4339,18 +4339,6 @@ MinorMarkCompactCollector::~MinorMarkCompactCollector() {
delete main_marking_visitor_; 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() { void MinorMarkCompactCollector::CleanupSweepToIteratePages() {
for (Page* p : sweep_to_iterate_pages_) { for (Page* p : sweep_to_iterate_pages_) {
if (p->IsFlagSet(Page::SWEEP_TO_ITERATE)) { if (p->IsFlagSet(Page::SWEEP_TO_ITERATE)) {
...@@ -4733,43 +4721,25 @@ MinorMarkCompactCollector::CreateRememberedSetUpdatingItem( ...@@ -4733,43 +4721,25 @@ MinorMarkCompactCollector::CreateRememberedSetUpdatingItem(
heap(), non_atomic_marking_state(), chunk, updating_mode); heap(), non_atomic_marking_state(), chunk, updating_mode);
} }
class MarkingItem;
class PageMarkingItem; class PageMarkingItem;
class RootMarkingItem; class RootMarkingItem;
class YoungGenerationMarkingTask; class YoungGenerationMarkingTask;
class MarkingItem : public ItemParallelJob::Item { class YoungGenerationMarkingTask {
public:
~MarkingItem() override = default;
virtual void Process(YoungGenerationMarkingTask* task) = 0;
};
class YoungGenerationMarkingTask : public ItemParallelJob::Task {
public: public:
YoungGenerationMarkingTask( YoungGenerationMarkingTask(
Isolate* isolate, MinorMarkCompactCollector* collector, Isolate* isolate, MinorMarkCompactCollector* collector,
MinorMarkCompactCollector::MarkingWorklist* global_worklist, int task_id) MinorMarkCompactCollector::MarkingWorklist* global_worklist, int task_id)
: ItemParallelJob::Task(isolate), : marking_worklist_(global_worklist, task_id),
collector_(collector),
marking_worklist_(global_worklist, task_id),
marking_state_(collector->marking_state()), marking_state_(collector->marking_state()),
visitor_(marking_state_, global_worklist, task_id) { visitor_(marking_state_, global_worklist, task_id) {
local_live_bytes_.reserve(isolate->heap()->new_space()->Capacity() / local_live_bytes_.reserve(isolate->heap()->new_space()->Capacity() /
Page::kPageSize); Page::kPageSize);
} }
void RunInParallel(Runner runner) override { int slots() const { return slots_; }
if (runner == Runner::kForeground) {
TRACE_GC(collector_->heap()->tracer(), void IncrementSlots() { ++slots_; }
GCTracer::Scope::MINOR_MC_MARK_PARALLEL);
ProcessItems();
} else {
TRACE_BACKGROUND_GC(
collector_->heap()->tracer(),
GCTracer::BackgroundScope::MINOR_MC_BACKGROUND_MARKING);
ProcessItems();
}
}
void MarkObject(Object object) { void MarkObject(Object object) {
if (!Heap::InYoungGeneration(object)) return; if (!Heap::InYoungGeneration(object)) return;
...@@ -4780,34 +4750,6 @@ class YoungGenerationMarkingTask : public ItemParallelJob::Task { ...@@ -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() { void EmptyMarkingWorklist() {
HeapObject object; HeapObject object;
while (marking_worklist_.Pop(&object)) { while (marking_worklist_.Pop(&object)) {
...@@ -4826,20 +4768,20 @@ class YoungGenerationMarkingTask : public ItemParallelJob::Task { ...@@ -4826,20 +4768,20 @@ class YoungGenerationMarkingTask : public ItemParallelJob::Task {
} }
} }
MinorMarkCompactCollector* collector_; private:
MinorMarkCompactCollector::MarkingWorklist::View marking_worklist_; MinorMarkCompactCollector::MarkingWorklist::View marking_worklist_;
MinorMarkCompactCollector::MarkingState* marking_state_; MinorMarkCompactCollector::MarkingState* marking_state_;
YoungGenerationMarkingVisitor visitor_; YoungGenerationMarkingVisitor visitor_;
std::unordered_map<Page*, intptr_t, Page::Hasher> local_live_bytes_; std::unordered_map<Page*, intptr_t, Page::Hasher> local_live_bytes_;
int slots_ = 0;
}; };
class PageMarkingItem : public MarkingItem { class PageMarkingItem : public ParallelWorkItem {
public: public:
explicit PageMarkingItem(MemoryChunk* chunk, std::atomic<int>* global_slots) explicit PageMarkingItem(MemoryChunk* chunk) : chunk_(chunk) {}
: chunk_(chunk), global_slots_(global_slots), slots_(0) {} ~PageMarkingItem() = default;
~PageMarkingItem() override { *global_slots_ = *global_slots_ + slots_; }
void Process(YoungGenerationMarkingTask* task) override { void Process(YoungGenerationMarkingTask* task) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.gc"), TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.gc"),
"PageMarkingItem::Process"); "PageMarkingItem::Process");
base::MutexGuard guard(chunk_->mutex()); base::MutexGuard guard(chunk_->mutex());
...@@ -4896,23 +4838,102 @@ class PageMarkingItem : public MarkingItem { ...@@ -4896,23 +4838,102 @@ class PageMarkingItem : public MarkingItem {
USE(success); USE(success);
DCHECK(success); DCHECK(success);
task->MarkObject(heap_object); task->MarkObject(heap_object);
slots_++; task->IncrementSlots();
return KEEP_SLOT; return KEEP_SLOT;
} }
return REMOVE_SLOT; return REMOVE_SLOT;
} }
MemoryChunk* chunk_; 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( void MinorMarkCompactCollector::MarkRootSetInParallel(
RootMarkingVisitor* root_visitor) { RootMarkingVisitor* root_visitor) {
std::atomic<int> slots; std::atomic<int> slots;
{ {
ItemParallelJob job(isolate()->cancelable_task_manager(), std::vector<PageMarkingItem> marking_items;
&page_parallel_job_semaphore_);
// Seed the root set (roots + old->new set). // Seed the root set (roots + old->new set).
{ {
...@@ -4930,22 +4951,25 @@ void MinorMarkCompactCollector::MarkRootSetInParallel( ...@@ -4930,22 +4951,25 @@ void MinorMarkCompactCollector::MarkRootSetInParallel(
root_visitor); root_visitor);
// Create items for each page. // Create items for each page.
RememberedSet<OLD_TO_NEW>::IterateMemoryChunks( RememberedSet<OLD_TO_NEW>::IterateMemoryChunks(
heap(), [&job, &slots](MemoryChunk* chunk) { heap(), [&marking_items](MemoryChunk* chunk) {
job.AddItem(new PageMarkingItem(chunk, &slots)); marking_items.emplace_back(chunk);
}); });
} }
// Add tasks and run in parallel. // 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); TRACE_GC(heap()->tracer(), GCTracer::Scope::MINOR_MC_MARK_ROOTS);
const int new_space_pages = V8::GetCurrentPlatform()
static_cast<int>(heap()->new_space()->Capacity()) / Page::kPageSize; ->PostJob(v8::TaskPriority::kUserBlocking,
const int num_tasks = NumberOfParallelMarkingTasks(new_space_pages); std::make_unique<YoungGenerationMarkingJob>(
for (int i = 0; i < num_tasks; i++) { isolate(), this, worklist(), std::move(marking_items),
job.AddTask( &slots))
new YoungGenerationMarkingTask(isolate(), this, worklist(), i)); ->Join();
}
job.Run();
DCHECK(worklist()->IsEmpty()); DCHECK(worklist()->IsEmpty());
} }
} }
......
...@@ -858,8 +858,6 @@ class MinorMarkCompactCollector final : public MarkCompactCollectorBase { ...@@ -858,8 +858,6 @@ class MinorMarkCompactCollector final : public MarkCompactCollectorBase {
std::unique_ptr<UpdatingItem> CreateRememberedSetUpdatingItem( std::unique_ptr<UpdatingItem> CreateRememberedSetUpdatingItem(
MemoryChunk* chunk, RememberedSetUpdatingMode updating_mode) override; MemoryChunk* chunk, RememberedSetUpdatingMode updating_mode) override;
int NumberOfParallelMarkingTasks(int pages);
void SweepArrayBufferExtensions(); void SweepArrayBufferExtensions();
MarkingWorklist* worklist_; MarkingWorklist* worklist_;
...@@ -873,6 +871,7 @@ class MinorMarkCompactCollector final : public MarkCompactCollectorBase { ...@@ -873,6 +871,7 @@ class MinorMarkCompactCollector final : public MarkCompactCollectorBase {
NonAtomicMarkingState non_atomic_marking_state_; NonAtomicMarkingState non_atomic_marking_state_;
friend class YoungGenerationMarkingTask; friend class YoungGenerationMarkingTask;
friend class YoungGenerationMarkingJob;
friend class YoungGenerationMarkingVisitor; 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