[heap] Visit Ephemerons in Parallel

Use ItemParallelJob to walk ephemerons in parallel.

Bug: chromium:844008
Change-Id: Iffc72422f7577458437764f42d13d3f2ee020758
Reviewed-on: https://chromium-review.googlesource.com/1100825
Commit-Queue: Dominik Inführ <dinfuehr@google.com>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53736}
This commit is contained in:
Dominik Inführ 2018-06-14 16:21:33 +02:00 committed by Commit Bot
parent 17db4a30cc
commit 9c22f3d7e1
6 changed files with 124 additions and 17 deletions

View File

@ -689,6 +689,8 @@ DEFINE_BOOL(concurrent_marking, V8_CONCURRENT_MARKING_BOOL,
"use concurrent marking")
DEFINE_BOOL(parallel_marking, true, "use parallel marking in atomic pause")
DEFINE_IMPLICATION(parallel_marking, concurrent_marking)
DEFINE_BOOL(parallel_ephemeron_visiting, true,
"use parallel visiting of ephemerons in atomic pause")
DEFINE_BOOL(
parallel_ephemeron_marking, true,
"use parallel marking of objects after visiting ephemerons in atomic pause")

View File

@ -690,6 +690,13 @@ bool ConcurrentMarking::Stop(StopRequest stop_request) {
return true;
}
bool ConcurrentMarking::IsStopped() {
if (!FLAG_concurrent_marking) return true;
base::LockGuard<base::Mutex> guard(&pending_lock_);
return pending_task_count_ == 0;
}
void ConcurrentMarking::FlushLiveBytes(
MajorNonAtomicMarkingState* marking_state) {
DCHECK_EQ(pending_task_count_, 0);

View File

@ -81,6 +81,9 @@ class ConcurrentMarking {
int TaskCount() { return task_count_; }
// Checks if all threads are stopped.
bool IsStopped();
size_t TotalMarkedBytes();
private:

View File

@ -2502,6 +2502,7 @@ class Heap {
// Classes in "heap" can be friends.
friend class AlwaysAllocateScope;
friend class ConcurrentMarking;
friend class EphemeronHashTableMarkingTask;
friend class GCCallbacksScope;
friend class GCTracer;
friend class HeapController;

View File

@ -1874,27 +1874,114 @@ void MarkCompactCollector::TrimEnumCache(Map* map,
heap_->RightTrimFixedArray(indices, to_trim);
}
void MarkCompactCollector::ProcessWeakCollections() {
MarkCompactMarkingVisitor visitor(this, marking_state());
class EphemeronHashTableMarkingItem : public ItemParallelJob::Item {
public:
explicit EphemeronHashTableMarkingItem(EphemeronHashTable* table, int offset)
: table_(table), offset_(offset) {}
virtual ~EphemeronHashTableMarkingItem() {}
EphemeronHashTable* table() const { return table_; }
int offset() const { return offset_; }
weak_objects_.ephemeron_hash_tables.Iterate([&](EphemeronHashTable* table) {
for (int i = 0; i < table->Capacity(); i++) {
HeapObject* heap_object = HeapObject::cast(table->KeyAt(i));
if (non_atomic_marking_state()->IsBlackOrGrey(heap_object)) {
private:
EphemeronHashTable* table_;
int offset_;
};
class EphemeronHashTableMarkingTask : public ItemParallelJob::Task {
public:
EphemeronHashTableMarkingTask(
Isolate* isolate, MarkCompactCollector* collector,
MarkCompactCollector::MarkingWorklist::ConcurrentMarkingWorklist*
worklist,
int task_id)
: ItemParallelJob::Task(isolate),
collector_(collector),
worklist_(worklist),
task_id_(task_id) {}
void RunInParallel() override {
EphemeronHashTableMarkingItem* item = nullptr;
while ((item = GetItem<EphemeronHashTableMarkingItem>()) != nullptr) {
EphemeronHashTable* table = item->table();
int start = item->offset();
int limit = Min(start + MarkCompactCollector::kEphemeronChunkSize,
table->Capacity());
for (int i = start; i < limit; i++) {
HeapObject* key = HeapObject::cast(table->KeyAt(i));
if (collector_->marking_state()->IsBlackOrGrey(key)) {
Object** key_slot =
table->RawFieldOfElementAt(EphemeronHashTable::EntryToIndex(i));
RecordSlot(table, key_slot, heap_object);
collector_->RecordSlot(table, key_slot, key);
Object** value_slot = table->RawFieldOfElementAt(
EphemeronHashTable::EntryToValueIndex(i));
if (V8_UNLIKELY(FLAG_track_retaining_path) &&
(*value_slot)->IsHeapObject()) {
heap()->AddEphemeronRetainer(heap_object,
HeapObject::cast(*value_slot));
Object* value_obj = *value_slot;
if (value_obj->IsHeapObject()) {
HeapObject* value = HeapObject::cast(value_obj);
collector_->RecordSlot(table, value_slot, value);
if (collector_->marking_state()->WhiteToGrey(value)) {
worklist_->Push(task_id_, value);
if (V8_UNLIKELY(FLAG_track_retaining_path)) {
collector_->heap()->AddEphemeronRetainer(key, value);
collector_->heap()->AddRetainer(table, value);
}
visitor.VisitPointer(table, value_slot);
}
}
}
}
item->MarkFinished();
}
worklist_->FlushToGlobal(task_id_);
}
private:
MarkCompactCollector* collector_;
MarkCompactCollector::MarkingWorklist::ConcurrentMarkingWorklist* worklist_;
int task_id_;
};
void MarkCompactCollector::ProcessWeakCollections() {
CHECK(heap()->concurrent_marking()->IsStopped());
ItemParallelJob marking_job(isolate()->cancelable_task_manager(),
&page_parallel_job_semaphore_);
size_t elements = 0;
// Split EphemeronHashTables into chunks such that we can divide work more
// equally between tasks
weak_objects_.ephemeron_hash_tables.Iterate([&](EphemeronHashTable* table) {
int capacity = table->Capacity();
int chunks = (capacity + kEphemeronChunkSize - 1) / kEphemeronChunkSize;
elements += static_cast<size_t>(capacity);
for (int i = 0; i < chunks; i++) {
marking_job.AddItem(
new EphemeronHashTableMarkingItem(table, i * kEphemeronChunkSize));
}
});
int num_tasks = NumberOfParallelEphemeronVisitingTasks(elements);
for (int i = 0; i < num_tasks; i++) {
marking_job.AddTask(new EphemeronHashTableMarkingTask(
isolate(), this, marking_worklist_.shared(), i));
}
marking_job.Run(isolate()->async_counters());
}
int MarkCompactCollector::NumberOfParallelEphemeronVisitingTasks(
size_t elements) {
DCHECK_GE(elements, 0);
if (!FLAG_parallel_ephemeron_visiting || elements == 0) return 1;
size_t chunks = (elements + kEphemeronChunkSize - 1) / kEphemeronChunkSize;
const size_t kMaxNumTasks =
MarkingWorklist::ConcurrentMarkingWorklist::kMaxNumTasks;
return Min(NumberOfAvailableCores(),
static_cast<int>(Min(chunks, kMaxNumTasks)));
}
void MarkCompactCollector::ClearWeakCollections() {
@ -3494,7 +3581,9 @@ int MinorMarkCompactCollector::NumberOfParallelMarkingTasks(int pages) {
// amount of marking that is required.
const int kPagesPerTask = 2;
const int wanted_tasks = Max(1, pages / kPagesPerTask);
return Min(NumberOfAvailableCores(), Min(wanted_tasks, kNumMarkers));
return Min(NumberOfAvailableCores(),
Min(wanted_tasks,
MinorMarkCompactCollector::MarkingWorklist::kMaxNumTasks));
}
void MinorMarkCompactCollector::CleanupSweepToIteratePages() {

View File

@ -770,6 +770,10 @@ class MarkCompactCollector final : public MarkCompactCollectorBase {
void ClearMarkbitsInPagedSpace(PagedSpace* space);
void ClearMarkbitsInNewSpace(NewSpace* space);
static const int kEphemeronChunkSize = 8 * KB;
int NumberOfParallelEphemeronVisitingTasks(size_t elements);
base::Mutex mutex_;
base::Semaphore page_parallel_job_semaphore_;
@ -815,6 +819,7 @@ class MarkCompactCollector final : public MarkCompactCollectorBase {
MarkingState marking_state_;
NonAtomicMarkingState non_atomic_marking_state_;
friend class EphemeronHashTableMarkingTask;
friend class FullEvacuator;
friend class Heap;
friend class RecordMigratedSlotVisitor;