| Index: src/heap/mark-compact.cc
|
| diff --git a/src/heap/mark-compact.cc b/src/heap/mark-compact.cc
|
| index 3e5dafe345a481f08f88f20fd971db40e8894af7..1dccfda4654ac29612801699a753358a0012c620 100644
|
| --- a/src/heap/mark-compact.cc
|
| +++ b/src/heap/mark-compact.cc
|
| @@ -19,14 +19,13 @@
|
| #include "src/heap/incremental-marking.h"
|
| #include "src/heap/mark-compact-inl.h"
|
| #include "src/heap/object-stats.h"
|
| +#include "src/heap/objects-visiting.h"
|
| #include "src/heap/objects-visiting-inl.h"
|
| -#include "src/heap/objects-visiting.h"
|
| #include "src/heap/slots-buffer.h"
|
| #include "src/heap/spaces-inl.h"
|
| #include "src/ic/ic.h"
|
| #include "src/ic/stub-cache.h"
|
| #include "src/profiler/cpu-profiler.h"
|
| -#include "src/utils-inl.h"
|
| #include "src/v8.h"
|
|
|
| namespace v8 {
|
| @@ -321,7 +320,9 @@
|
| {
|
| GCTracer::Scope gc_scope(heap()->tracer(),
|
| GCTracer::Scope::MC_CLEAR_SLOTS_BUFFER);
|
| - for (Page* p : evacuation_candidates_) {
|
| + int number_of_pages = evacuation_candidates_.length();
|
| + for (int i = 0; i < number_of_pages; i++) {
|
| + Page* p = evacuation_candidates_[i];
|
| SlotsBuffer::RemoveInvalidSlots(heap_, p->slots_buffer());
|
| }
|
| }
|
| @@ -475,6 +476,30 @@
|
| Page::FromAddress(obj->address())->ResetLiveBytes();
|
| }
|
| }
|
| +
|
| +
|
| +class MarkCompactCollector::CompactionTask : public CancelableTask {
|
| + public:
|
| + explicit CompactionTask(Heap* heap, CompactionSpaceCollection* spaces)
|
| + : CancelableTask(heap->isolate()), spaces_(spaces) {}
|
| +
|
| + virtual ~CompactionTask() {}
|
| +
|
| + private:
|
| + // v8::internal::CancelableTask overrides.
|
| + void RunInternal() override {
|
| + MarkCompactCollector* mark_compact =
|
| + isolate()->heap()->mark_compact_collector();
|
| + SlotsBuffer* evacuation_slots_buffer = nullptr;
|
| + mark_compact->EvacuatePages(spaces_, &evacuation_slots_buffer);
|
| + mark_compact->AddEvacuationSlotsBufferSynchronized(evacuation_slots_buffer);
|
| + mark_compact->pending_compaction_tasks_semaphore_.Signal();
|
| + }
|
| +
|
| + CompactionSpaceCollection* spaces_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(CompactionTask);
|
| +};
|
|
|
|
|
| class MarkCompactCollector::SweeperTask : public v8::Task {
|
| @@ -806,7 +831,9 @@
|
|
|
| void MarkCompactCollector::AbortCompaction() {
|
| if (compacting_) {
|
| - for (Page* p : evacuation_candidates_) {
|
| + int npages = evacuation_candidates_.length();
|
| + for (int i = 0; i < npages; i++) {
|
| + Page* p = evacuation_candidates_[i];
|
| slots_buffer_allocator_->DeallocateChain(p->slots_buffer_address());
|
| p->ClearEvacuationCandidate();
|
| p->ClearFlag(MemoryChunk::RESCAN_ON_EVACUATION);
|
| @@ -1521,13 +1548,8 @@
|
| class MarkCompactCollector::EvacuateVisitorBase
|
| : public MarkCompactCollector::HeapObjectVisitor {
|
| public:
|
| - EvacuateVisitorBase(Heap* heap, CompactionSpaceCollection* compaction_spaces,
|
| - SlotsBuffer** evacuation_slots_buffer,
|
| - LocalStoreBuffer* local_store_buffer)
|
| - : heap_(heap),
|
| - evacuation_slots_buffer_(evacuation_slots_buffer),
|
| - compaction_spaces_(compaction_spaces),
|
| - local_store_buffer_(local_store_buffer) {}
|
| + EvacuateVisitorBase(Heap* heap, SlotsBuffer** evacuation_slots_buffer)
|
| + : heap_(heap), evacuation_slots_buffer_(evacuation_slots_buffer) {}
|
|
|
| bool TryEvacuateObject(PagedSpace* target_space, HeapObject* object,
|
| HeapObject** target_object) {
|
| @@ -1537,7 +1559,7 @@
|
| if (allocation.To(target_object)) {
|
| heap_->mark_compact_collector()->MigrateObject(
|
| *target_object, object, size, target_space->identity(),
|
| - evacuation_slots_buffer_, local_store_buffer_);
|
| + evacuation_slots_buffer_);
|
| return true;
|
| }
|
| return false;
|
| @@ -1546,8 +1568,6 @@
|
| protected:
|
| Heap* heap_;
|
| SlotsBuffer** evacuation_slots_buffer_;
|
| - CompactionSpaceCollection* compaction_spaces_;
|
| - LocalStoreBuffer* local_store_buffer_;
|
| };
|
|
|
|
|
| @@ -1558,12 +1578,9 @@
|
| static const intptr_t kMaxLabObjectSize = 256;
|
|
|
| explicit EvacuateNewSpaceVisitor(Heap* heap,
|
| - CompactionSpaceCollection* compaction_spaces,
|
| SlotsBuffer** evacuation_slots_buffer,
|
| - LocalStoreBuffer* local_store_buffer,
|
| HashMap* local_pretenuring_feedback)
|
| - : EvacuateVisitorBase(heap, compaction_spaces, evacuation_slots_buffer,
|
| - local_store_buffer),
|
| + : EvacuateVisitorBase(heap, evacuation_slots_buffer),
|
| buffer_(LocalAllocationBuffer::InvalidBuffer()),
|
| space_to_allocate_(NEW_SPACE),
|
| promoted_size_(0),
|
| @@ -1575,8 +1592,7 @@
|
| int size = object->Size();
|
| HeapObject* target_object = nullptr;
|
| if (heap_->ShouldBePromoted(object->address(), size) &&
|
| - TryEvacuateObject(compaction_spaces_->Get(OLD_SPACE), object,
|
| - &target_object)) {
|
| + TryEvacuateObject(heap_->old_space(), object, &target_object)) {
|
| // If we end up needing more special cases, we should factor this out.
|
| if (V8_UNLIKELY(target_object->IsJSArrayBuffer())) {
|
| heap_->array_buffer_tracker()->Promote(
|
| @@ -1589,8 +1605,7 @@
|
| AllocationSpace space = AllocateTargetObject(object, &target);
|
| heap_->mark_compact_collector()->MigrateObject(
|
| HeapObject::cast(target), object, size, space,
|
| - (space == NEW_SPACE) ? nullptr : evacuation_slots_buffer_,
|
| - (space == NEW_SPACE) ? nullptr : local_store_buffer_);
|
| + (space == NEW_SPACE) ? nullptr : evacuation_slots_buffer_);
|
| if (V8_UNLIKELY(target->IsJSArrayBuffer())) {
|
| heap_->array_buffer_tracker()->MarkLive(JSArrayBuffer::cast(target));
|
| }
|
| @@ -1663,8 +1678,7 @@
|
| inline AllocationResult AllocateInOldSpace(int size_in_bytes,
|
| AllocationAlignment alignment) {
|
| AllocationResult allocation =
|
| - compaction_spaces_->Get(OLD_SPACE)->AllocateRaw(size_in_bytes,
|
| - alignment);
|
| + heap_->old_space()->AllocateRaw(size_in_bytes, alignment);
|
| if (allocation.IsRetry()) {
|
| FatalProcessOutOfMemory(
|
| "MarkCompactCollector: semi-space copy, fallback in old gen\n");
|
| @@ -1710,10 +1724,9 @@
|
| public:
|
| EvacuateOldSpaceVisitor(Heap* heap,
|
| CompactionSpaceCollection* compaction_spaces,
|
| - SlotsBuffer** evacuation_slots_buffer,
|
| - LocalStoreBuffer* local_store_buffer)
|
| - : EvacuateVisitorBase(heap, compaction_spaces, evacuation_slots_buffer,
|
| - local_store_buffer) {}
|
| + SlotsBuffer** evacuation_slots_buffer)
|
| + : EvacuateVisitorBase(heap, evacuation_slots_buffer),
|
| + compaction_spaces_(compaction_spaces) {}
|
|
|
| bool Visit(HeapObject* object) override {
|
| CompactionSpace* target_space = compaction_spaces_->Get(
|
| @@ -1725,6 +1738,9 @@
|
| }
|
| return false;
|
| }
|
| +
|
| + private:
|
| + CompactionSpaceCollection* compaction_spaces_;
|
| };
|
|
|
|
|
| @@ -2532,14 +2548,14 @@
|
| heap()->set_encountered_transition_arrays(Smi::FromInt(0));
|
| }
|
|
|
| +
|
| void MarkCompactCollector::RecordMigratedSlot(
|
| - Object* value, Address slot, SlotsBuffer** evacuation_slots_buffer,
|
| - LocalStoreBuffer* local_store_buffer) {
|
| + Object* value, Address slot, SlotsBuffer** evacuation_slots_buffer) {
|
| // When parallel compaction is in progress, store and slots buffer entries
|
| // require synchronization.
|
| if (heap_->InNewSpace(value)) {
|
| if (compaction_in_progress_) {
|
| - local_store_buffer->Record(slot);
|
| + heap_->store_buffer()->MarkSynchronized(slot);
|
| } else {
|
| heap_->store_buffer()->Mark(slot);
|
| }
|
| @@ -2621,23 +2637,19 @@
|
| class RecordMigratedSlotVisitor final : public ObjectVisitor {
|
| public:
|
| RecordMigratedSlotVisitor(MarkCompactCollector* collector,
|
| - SlotsBuffer** evacuation_slots_buffer,
|
| - LocalStoreBuffer* local_store_buffer)
|
| + SlotsBuffer** evacuation_slots_buffer)
|
| : collector_(collector),
|
| - evacuation_slots_buffer_(evacuation_slots_buffer),
|
| - local_store_buffer_(local_store_buffer) {}
|
| + evacuation_slots_buffer_(evacuation_slots_buffer) {}
|
|
|
| V8_INLINE void VisitPointer(Object** p) override {
|
| collector_->RecordMigratedSlot(*p, reinterpret_cast<Address>(p),
|
| - evacuation_slots_buffer_,
|
| - local_store_buffer_);
|
| + evacuation_slots_buffer_);
|
| }
|
|
|
| V8_INLINE void VisitPointers(Object** start, Object** end) override {
|
| while (start < end) {
|
| collector_->RecordMigratedSlot(*start, reinterpret_cast<Address>(start),
|
| - evacuation_slots_buffer_,
|
| - local_store_buffer_);
|
| + evacuation_slots_buffer_);
|
| ++start;
|
| }
|
| }
|
| @@ -2653,7 +2665,6 @@
|
| private:
|
| MarkCompactCollector* collector_;
|
| SlotsBuffer** evacuation_slots_buffer_;
|
| - LocalStoreBuffer* local_store_buffer_;
|
| };
|
|
|
|
|
| @@ -2671,10 +2682,9 @@
|
| // pointer iteration. This is an issue if the store buffer overflows and we
|
| // have to scan the entire old space, including dead objects, looking for
|
| // pointers to new space.
|
| -void MarkCompactCollector::MigrateObject(HeapObject* dst, HeapObject* src,
|
| - int size, AllocationSpace dest,
|
| - SlotsBuffer** evacuation_slots_buffer,
|
| - LocalStoreBuffer* local_store_buffer) {
|
| +void MarkCompactCollector::MigrateObject(
|
| + HeapObject* dst, HeapObject* src, int size, AllocationSpace dest,
|
| + SlotsBuffer** evacuation_slots_buffer) {
|
| Address dst_addr = dst->address();
|
| Address src_addr = src->address();
|
| DCHECK(heap()->AllowedToBeMigrated(src, dest));
|
| @@ -2685,8 +2695,7 @@
|
| DCHECK(IsAligned(size, kPointerSize));
|
|
|
| heap()->MoveBlock(dst->address(), src->address(), size);
|
| - RecordMigratedSlotVisitor visitor(this, evacuation_slots_buffer,
|
| - local_store_buffer);
|
| + RecordMigratedSlotVisitor visitor(this, evacuation_slots_buffer);
|
| dst->IterateBody(&visitor);
|
| } else if (dest == CODE_SPACE) {
|
| DCHECK_CODEOBJECT_SIZE(size, heap()->code_space());
|
| @@ -3048,18 +3057,54 @@
|
|
|
|
|
| void MarkCompactCollector::EvacuateNewSpacePrologue() {
|
| + // There are soft limits in the allocation code, designed trigger a mark
|
| + // sweep collection by failing allocations. But since we are already in
|
| + // a mark-sweep allocation, there is no sense in trying to trigger one.
|
| + AlwaysAllocateScope scope(isolate());
|
| +
|
| NewSpace* new_space = heap()->new_space();
|
| - NewSpacePageIterator it(new_space->bottom(), new_space->top());
|
| - // Append the list of new space pages to be processed.
|
| +
|
| + // Store allocation range before flipping semispaces.
|
| + Address from_bottom = new_space->bottom();
|
| + Address from_top = new_space->top();
|
| +
|
| + // Flip the semispaces. After flipping, to space is empty, from space has
|
| + // live objects.
|
| + new_space->Flip();
|
| + new_space->ResetAllocationInfo();
|
| +
|
| + newspace_evacuation_candidates_.Clear();
|
| + NewSpacePageIterator it(from_bottom, from_top);
|
| while (it.has_next()) {
|
| newspace_evacuation_candidates_.Add(it.next());
|
| }
|
| - new_space->Flip();
|
| - new_space->ResetAllocationInfo();
|
| -}
|
| -
|
| -void MarkCompactCollector::EvacuateNewSpaceEpilogue() {
|
| - newspace_evacuation_candidates_.Rewind(0);
|
| +}
|
| +
|
| +
|
| +HashMap* MarkCompactCollector::EvacuateNewSpaceInParallel() {
|
| + HashMap* local_pretenuring_feedback = new HashMap(
|
| + HashMap::PointersMatch, kInitialLocalPretenuringFeedbackCapacity);
|
| + EvacuateNewSpaceVisitor new_space_visitor(heap(), &migration_slots_buffer_,
|
| + local_pretenuring_feedback);
|
| + // First pass: traverse all objects in inactive semispace, remove marks,
|
| + // migrate live objects and write forwarding addresses. This stage puts
|
| + // new entries in the store buffer and may cause some pages to be marked
|
| + // scan-on-scavenge.
|
| + for (int i = 0; i < newspace_evacuation_candidates_.length(); i++) {
|
| + NewSpacePage* p =
|
| + reinterpret_cast<NewSpacePage*>(newspace_evacuation_candidates_[i]);
|
| + bool ok = VisitLiveObjects(p, &new_space_visitor, kClearMarkbits);
|
| + USE(ok);
|
| + DCHECK(ok);
|
| + }
|
| + heap_->IncrementPromotedObjectsSize(
|
| + static_cast<int>(new_space_visitor.promoted_size()));
|
| + heap_->IncrementSemiSpaceCopiedObjectSize(
|
| + static_cast<int>(new_space_visitor.semispace_copied_size()));
|
| + heap_->IncrementYoungSurvivorsCounter(
|
| + static_cast<int>(new_space_visitor.promoted_size()) +
|
| + static_cast<int>(new_space_visitor.semispace_copied_size()));
|
| + return local_pretenuring_feedback;
|
| }
|
|
|
|
|
| @@ -3069,168 +3114,8 @@
|
| evacuation_slots_buffers_.Add(evacuation_slots_buffer);
|
| }
|
|
|
| -class MarkCompactCollector::Evacuator : public Malloced {
|
| - public:
|
| - Evacuator(MarkCompactCollector* collector,
|
| - const List<Page*>& evacuation_candidates,
|
| - const List<NewSpacePage*>& newspace_evacuation_candidates)
|
| - : collector_(collector),
|
| - evacuation_candidates_(evacuation_candidates),
|
| - newspace_evacuation_candidates_(newspace_evacuation_candidates),
|
| - compaction_spaces_(collector->heap()),
|
| - local_slots_buffer_(nullptr),
|
| - local_store_buffer_(),
|
| - local_pretenuring_feedback_(HashMap::PointersMatch,
|
| - kInitialLocalPretenuringFeedbackCapacity),
|
| - new_space_visitor_(collector->heap(), &compaction_spaces_,
|
| - &local_slots_buffer_, &local_store_buffer_,
|
| - &local_pretenuring_feedback_),
|
| - old_space_visitor_(collector->heap(), &compaction_spaces_,
|
| - &local_slots_buffer_, &local_store_buffer_),
|
| - duration_(0.0),
|
| - bytes_compacted_(0),
|
| - task_id_(0) {}
|
| -
|
| - // Evacuate the configured set of pages in parallel.
|
| - inline void EvacuatePages();
|
| -
|
| - // Merge back locally cached info sequentially. Note that this method needs
|
| - // to be called from the main thread.
|
| - inline void Finalize();
|
| -
|
| - CompactionSpaceCollection* compaction_spaces() { return &compaction_spaces_; }
|
| -
|
| - uint32_t task_id() { return task_id_; }
|
| - void set_task_id(uint32_t id) { task_id_ = id; }
|
| -
|
| - private:
|
| - static const int kInitialLocalPretenuringFeedbackCapacity = 256;
|
| -
|
| - Heap* heap() { return collector_->heap(); }
|
| -
|
| - void ReportCompactionProgress(double duration, intptr_t bytes_compacted) {
|
| - duration_ += duration;
|
| - bytes_compacted_ += bytes_compacted;
|
| - }
|
| -
|
| - inline bool EvacuateSinglePage(MemoryChunk* p, HeapObjectVisitor* visitor);
|
| -
|
| - MarkCompactCollector* collector_;
|
| -
|
| - // Pages to process.
|
| - const List<Page*>& evacuation_candidates_;
|
| - const List<NewSpacePage*>& newspace_evacuation_candidates_;
|
| -
|
| - // Locally cached collector data.
|
| - CompactionSpaceCollection compaction_spaces_;
|
| - SlotsBuffer* local_slots_buffer_;
|
| - LocalStoreBuffer local_store_buffer_;
|
| - HashMap local_pretenuring_feedback_;
|
| -
|
| - // Vistors for the corresponding spaces.
|
| - EvacuateNewSpaceVisitor new_space_visitor_;
|
| - EvacuateOldSpaceVisitor old_space_visitor_;
|
| -
|
| - // Book keeping info.
|
| - double duration_;
|
| - intptr_t bytes_compacted_;
|
| -
|
| - // Task id, if this evacuator is executed on a background task instead of
|
| - // the main thread. Can be used to try to abort the task currently scheduled
|
| - // to executed to evacuate pages.
|
| - uint32_t task_id_;
|
| -};
|
| -
|
| -bool MarkCompactCollector::Evacuator::EvacuateSinglePage(
|
| - MemoryChunk* p, HeapObjectVisitor* visitor) {
|
| - bool success = true;
|
| - if (p->parallel_compaction_state().TrySetValue(
|
| - MemoryChunk::kCompactingDone, MemoryChunk::kCompactingInProgress)) {
|
| - if (p->IsEvacuationCandidate() || p->InNewSpace()) {
|
| - DCHECK_EQ(p->parallel_compaction_state().Value(),
|
| - MemoryChunk::kCompactingInProgress);
|
| - int saved_live_bytes = p->LiveBytes();
|
| - double evacuation_time;
|
| - {
|
| - AlwaysAllocateScope always_allocate(heap()->isolate());
|
| - TimedScope timed_scope(&evacuation_time);
|
| - success = collector_->VisitLiveObjects(p, visitor, kClearMarkbits);
|
| - }
|
| - if (success) {
|
| - ReportCompactionProgress(evacuation_time, saved_live_bytes);
|
| - p->parallel_compaction_state().SetValue(
|
| - MemoryChunk::kCompactingFinalize);
|
| - } else {
|
| - p->parallel_compaction_state().SetValue(
|
| - MemoryChunk::kCompactingAborted);
|
| - }
|
| - } else {
|
| - // There could be popular pages in the list of evacuation candidates
|
| - // which we do not compact.
|
| - p->parallel_compaction_state().SetValue(MemoryChunk::kCompactingDone);
|
| - }
|
| - }
|
| - return success;
|
| -}
|
| -
|
| -void MarkCompactCollector::Evacuator::EvacuatePages() {
|
| - for (NewSpacePage* p : newspace_evacuation_candidates_) {
|
| - DCHECK(p->InNewSpace());
|
| - DCHECK_EQ(p->concurrent_sweeping_state().Value(),
|
| - NewSpacePage::kSweepingDone);
|
| - bool success = EvacuateSinglePage(p, &new_space_visitor_);
|
| - DCHECK(success);
|
| - USE(success);
|
| - }
|
| - for (Page* p : evacuation_candidates_) {
|
| - DCHECK(p->IsEvacuationCandidate() ||
|
| - p->IsFlagSet(MemoryChunk::RESCAN_ON_EVACUATION));
|
| - DCHECK_EQ(p->concurrent_sweeping_state().Value(), Page::kSweepingDone);
|
| - EvacuateSinglePage(p, &old_space_visitor_);
|
| - }
|
| -}
|
| -
|
| -void MarkCompactCollector::Evacuator::Finalize() {
|
| - heap()->old_space()->MergeCompactionSpace(compaction_spaces_.Get(OLD_SPACE));
|
| - heap()->code_space()->MergeCompactionSpace(
|
| - compaction_spaces_.Get(CODE_SPACE));
|
| - heap()->tracer()->AddCompactionEvent(duration_, bytes_compacted_);
|
| - heap()->IncrementPromotedObjectsSize(new_space_visitor_.promoted_size());
|
| - heap()->IncrementSemiSpaceCopiedObjectSize(
|
| - new_space_visitor_.semispace_copied_size());
|
| - heap()->IncrementYoungSurvivorsCounter(
|
| - new_space_visitor_.promoted_size() +
|
| - new_space_visitor_.semispace_copied_size());
|
| - heap()->MergeAllocationSitePretenuringFeedback(local_pretenuring_feedback_);
|
| - local_store_buffer_.Process(heap()->store_buffer());
|
| - collector_->AddEvacuationSlotsBufferSynchronized(local_slots_buffer_);
|
| -}
|
| -
|
| -class MarkCompactCollector::CompactionTask : public CancelableTask {
|
| - public:
|
| - explicit CompactionTask(Heap* heap, Evacuator* evacuator)
|
| - : CancelableTask(heap->isolate()), heap_(heap), evacuator_(evacuator) {
|
| - evacuator->set_task_id(id());
|
| - }
|
| -
|
| - virtual ~CompactionTask() {}
|
| -
|
| - private:
|
| - // v8::internal::CancelableTask overrides.
|
| - void RunInternal() override {
|
| - evacuator_->EvacuatePages();
|
| - heap_->mark_compact_collector()
|
| - ->pending_compaction_tasks_semaphore_.Signal();
|
| - }
|
| -
|
| - Heap* heap_;
|
| - Evacuator* evacuator_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(CompactionTask);
|
| -};
|
| -
|
| -int MarkCompactCollector::NumberOfParallelCompactionTasks(int pages,
|
| - intptr_t live_bytes) {
|
| +
|
| +int MarkCompactCollector::NumberOfParallelCompactionTasks() {
|
| if (!FLAG_parallel_compaction) return 1;
|
| // Compute the number of needed tasks based on a target compaction time, the
|
| // profiled compaction speed and marked live memory.
|
| @@ -3238,83 +3123,83 @@
|
| // The number of parallel compaction tasks is limited by:
|
| // - #evacuation pages
|
| // - (#cores - 1)
|
| + // - a hard limit
|
| const double kTargetCompactionTimeInMs = 1;
|
| - const int kNumSweepingTasks = 3;
|
| + const int kMaxCompactionTasks = 8;
|
|
|
| intptr_t compaction_speed =
|
| heap()->tracer()->CompactionSpeedInBytesPerMillisecond();
|
| -
|
| - const int available_cores =
|
| - Max(1, base::SysInfo::NumberOfProcessors() - kNumSweepingTasks - 1);
|
| - int tasks;
|
| - if (compaction_speed > 0) {
|
| - tasks = 1 + static_cast<int>(static_cast<double>(live_bytes) /
|
| - compaction_speed / kTargetCompactionTimeInMs);
|
| - } else {
|
| - tasks = pages;
|
| - }
|
| - const int tasks_capped_pages = Min(pages, tasks);
|
| - return Min(available_cores, tasks_capped_pages);
|
| -}
|
| -
|
| -
|
| -void MarkCompactCollector::EvacuatePagesInParallel() {
|
| - int num_pages = 0;
|
| + if (compaction_speed == 0) return 1;
|
| +
|
| intptr_t live_bytes = 0;
|
| for (Page* page : evacuation_candidates_) {
|
| - num_pages++;
|
| live_bytes += page->LiveBytes();
|
| }
|
| - for (NewSpacePage* page : newspace_evacuation_candidates_) {
|
| - num_pages++;
|
| - live_bytes += page->LiveBytes();
|
| - }
|
| - DCHECK_GE(num_pages, 1);
|
| +
|
| + const int cores = Max(1, base::SysInfo::NumberOfProcessors() - 1);
|
| + const int tasks =
|
| + 1 + static_cast<int>(static_cast<double>(live_bytes) / compaction_speed /
|
| + kTargetCompactionTimeInMs);
|
| + const int tasks_capped_pages = Min(evacuation_candidates_.length(), tasks);
|
| + const int tasks_capped_cores = Min(cores, tasks_capped_pages);
|
| + const int tasks_capped_hard = Min(kMaxCompactionTasks, tasks_capped_cores);
|
| + return tasks_capped_hard;
|
| +}
|
| +
|
| +
|
| +void MarkCompactCollector::EvacuatePagesInParallel() {
|
| + const int num_pages = evacuation_candidates_.length();
|
| + if (num_pages == 0) return;
|
|
|
| // Used for trace summary.
|
| + intptr_t live_bytes = 0;
|
| intptr_t compaction_speed = 0;
|
| if (FLAG_trace_fragmentation) {
|
| + for (Page* page : evacuation_candidates_) {
|
| + live_bytes += page->LiveBytes();
|
| + }
|
| compaction_speed = heap()->tracer()->CompactionSpeedInBytesPerMillisecond();
|
| }
|
| -
|
| - const int num_tasks = NumberOfParallelCompactionTasks(num_pages, live_bytes);
|
| + const int num_tasks = NumberOfParallelCompactionTasks();
|
|
|
| // Set up compaction spaces.
|
| - Evacuator** evacuators = new Evacuator*[num_tasks];
|
| CompactionSpaceCollection** compaction_spaces_for_tasks =
|
| new CompactionSpaceCollection*[num_tasks];
|
| for (int i = 0; i < num_tasks; i++) {
|
| - evacuators[i] = new Evacuator(this, evacuation_candidates_,
|
| - newspace_evacuation_candidates_);
|
| - compaction_spaces_for_tasks[i] = evacuators[i]->compaction_spaces();
|
| - }
|
| + compaction_spaces_for_tasks[i] = new CompactionSpaceCollection(heap());
|
| + }
|
| +
|
| heap()->old_space()->DivideUponCompactionSpaces(compaction_spaces_for_tasks,
|
| num_tasks);
|
| heap()->code_space()->DivideUponCompactionSpaces(compaction_spaces_for_tasks,
|
| num_tasks);
|
| +
|
| + uint32_t* task_ids = new uint32_t[num_tasks - 1];
|
| + // Kick off parallel tasks.
|
| + StartParallelCompaction(compaction_spaces_for_tasks, task_ids, num_tasks);
|
| + // Wait for unfinished and not-yet-started tasks.
|
| + WaitUntilCompactionCompleted(task_ids, num_tasks - 1);
|
| + delete[] task_ids;
|
| +
|
| + double compaction_duration = 0.0;
|
| + intptr_t compacted_memory = 0;
|
| + // Merge back memory (compacted and unused) from compaction spaces.
|
| + for (int i = 0; i < num_tasks; i++) {
|
| + heap()->old_space()->MergeCompactionSpace(
|
| + compaction_spaces_for_tasks[i]->Get(OLD_SPACE));
|
| + heap()->code_space()->MergeCompactionSpace(
|
| + compaction_spaces_for_tasks[i]->Get(CODE_SPACE));
|
| + compacted_memory += compaction_spaces_for_tasks[i]->bytes_compacted();
|
| + compaction_duration += compaction_spaces_for_tasks[i]->duration();
|
| + delete compaction_spaces_for_tasks[i];
|
| + }
|
| delete[] compaction_spaces_for_tasks;
|
| -
|
| - // Kick off parallel tasks.
|
| - StartParallelCompaction(evacuators, num_tasks);
|
| - // Wait for unfinished and not-yet-started tasks.
|
| - WaitUntilCompactionCompleted(&evacuators[1], num_tasks - 1);
|
| -
|
| - // Finalize local evacuators by merging back all locally cached data.
|
| - for (int i = 0; i < num_tasks; i++) {
|
| - evacuators[i]->Finalize();
|
| - delete evacuators[i];
|
| - }
|
| - delete[] evacuators;
|
| -
|
| - // Finalize pages sequentially.
|
| - for (NewSpacePage* p : newspace_evacuation_candidates_) {
|
| - DCHECK_EQ(p->parallel_compaction_state().Value(),
|
| - MemoryChunk::kCompactingFinalize);
|
| - p->parallel_compaction_state().SetValue(MemoryChunk::kCompactingDone);
|
| - }
|
| -
|
| + heap()->tracer()->AddCompactionEvent(compaction_duration, compacted_memory);
|
| +
|
| + // Finalize sequentially.
|
| int abandoned_pages = 0;
|
| - for (Page* p : evacuation_candidates_) {
|
| + for (int i = 0; i < num_pages; i++) {
|
| + Page* p = evacuation_candidates_[i];
|
| switch (p->parallel_compaction_state().Value()) {
|
| case MemoryChunk::ParallelCompactingState::kCompactingAborted:
|
| // We have partially compacted the page, i.e., some objects may have
|
| @@ -3347,7 +3232,7 @@
|
| DCHECK(p->IsFlagSet(Page::RESCAN_ON_EVACUATION));
|
| break;
|
| default:
|
| - // MemoryChunk::kCompactingInProgress.
|
| + // We should not observe kCompactingInProgress, or kCompactingDone.
|
| UNREACHABLE();
|
| }
|
| p->parallel_compaction_state().SetValue(MemoryChunk::kCompactingDone);
|
| @@ -3364,32 +3249,72 @@
|
| }
|
| }
|
|
|
| -void MarkCompactCollector::StartParallelCompaction(Evacuator** evacuators,
|
| - int len) {
|
| +
|
| +void MarkCompactCollector::StartParallelCompaction(
|
| + CompactionSpaceCollection** compaction_spaces, uint32_t* task_ids,
|
| + int len) {
|
| compaction_in_progress_ = true;
|
| for (int i = 1; i < len; i++) {
|
| - CompactionTask* task = new CompactionTask(heap(), evacuators[i]);
|
| + CompactionTask* task = new CompactionTask(heap(), compaction_spaces[i]);
|
| + task_ids[i - 1] = task->id();
|
| V8::GetCurrentPlatform()->CallOnBackgroundThread(
|
| task, v8::Platform::kShortRunningTask);
|
| }
|
|
|
| - // Contribute on main thread.
|
| - evacuators[0]->EvacuatePages();
|
| -}
|
| -
|
| -void MarkCompactCollector::WaitUntilCompactionCompleted(Evacuator** evacuators,
|
| + // Contribute in main thread.
|
| + EvacuatePages(compaction_spaces[0], &migration_slots_buffer_);
|
| +}
|
| +
|
| +
|
| +void MarkCompactCollector::WaitUntilCompactionCompleted(uint32_t* task_ids,
|
| int len) {
|
| // Try to cancel compaction tasks that have not been run (as they might be
|
| // stuck in a worker queue). Tasks that cannot be canceled, have either
|
| // already completed or are still running, hence we need to wait for their
|
| // semaphore signal.
|
| for (int i = 0; i < len; i++) {
|
| - if (!heap()->isolate()->cancelable_task_manager()->TryAbort(
|
| - evacuators[i]->task_id())) {
|
| + if (!heap()->isolate()->cancelable_task_manager()->TryAbort(task_ids[i])) {
|
| pending_compaction_tasks_semaphore_.Wait();
|
| }
|
| }
|
| compaction_in_progress_ = false;
|
| +}
|
| +
|
| +
|
| +void MarkCompactCollector::EvacuatePages(
|
| + CompactionSpaceCollection* compaction_spaces,
|
| + SlotsBuffer** evacuation_slots_buffer) {
|
| + EvacuateOldSpaceVisitor visitor(heap(), compaction_spaces,
|
| + evacuation_slots_buffer);
|
| + for (int i = 0; i < evacuation_candidates_.length(); i++) {
|
| + Page* p = evacuation_candidates_[i];
|
| + DCHECK(p->IsEvacuationCandidate() ||
|
| + p->IsFlagSet(Page::RESCAN_ON_EVACUATION));
|
| + DCHECK(p->SweepingDone());
|
| + if (p->parallel_compaction_state().TrySetValue(
|
| + MemoryChunk::kCompactingDone, MemoryChunk::kCompactingInProgress)) {
|
| + if (p->IsEvacuationCandidate()) {
|
| + DCHECK_EQ(p->parallel_compaction_state().Value(),
|
| + MemoryChunk::kCompactingInProgress);
|
| + double start = heap()->MonotonicallyIncreasingTimeInMs();
|
| + intptr_t live_bytes = p->LiveBytes();
|
| + AlwaysAllocateScope always_allocate(isolate());
|
| + if (VisitLiveObjects(p, &visitor, kClearMarkbits)) {
|
| + p->parallel_compaction_state().SetValue(
|
| + MemoryChunk::kCompactingFinalize);
|
| + compaction_spaces->ReportCompactionProgress(
|
| + heap()->MonotonicallyIncreasingTimeInMs() - start, live_bytes);
|
| + } else {
|
| + p->parallel_compaction_state().SetValue(
|
| + MemoryChunk::kCompactingAborted);
|
| + }
|
| + } else {
|
| + // There could be popular pages in the list of evacuation candidates
|
| + // which we do compact.
|
| + p->parallel_compaction_state().SetValue(MemoryChunk::kCompactingDone);
|
| + }
|
| + }
|
| + }
|
| }
|
|
|
|
|
| @@ -3535,7 +3460,9 @@
|
| Address end_slot) {
|
| // Remove entries by replacing them with an old-space slot containing a smi
|
| // that is located in an unmovable page.
|
| - for (Page* p : evacuation_candidates_) {
|
| + int npages = evacuation_candidates_.length();
|
| + for (int i = 0; i < npages; i++) {
|
| + Page* p = evacuation_candidates_[i];
|
| DCHECK(p->IsEvacuationCandidate() ||
|
| p->IsFlagSet(Page::RESCAN_ON_EVACUATION));
|
| if (p->IsEvacuationCandidate()) {
|
| @@ -3615,7 +3542,8 @@
|
|
|
| void MarkCompactCollector::SweepAbortedPages() {
|
| // Second pass on aborted pages.
|
| - for (Page* p : evacuation_candidates_) {
|
| + for (int i = 0; i < evacuation_candidates_.length(); i++) {
|
| + Page* p = evacuation_candidates_[i];
|
| if (p->IsFlagSet(Page::COMPACTION_WAS_ABORTED)) {
|
| p->ClearFlag(MemoryChunk::COMPACTION_WAS_ABORTED);
|
| p->concurrent_sweeping_state().SetValue(Page::kSweepingInProgress);
|
| @@ -3647,15 +3575,26 @@
|
| GCTracer::Scope gc_scope(heap()->tracer(), GCTracer::Scope::MC_EVACUATE);
|
| Heap::RelocationLock relocation_lock(heap());
|
|
|
| + HashMap* local_pretenuring_feedback = nullptr;
|
| {
|
| GCTracer::Scope gc_scope(heap()->tracer(),
|
| GCTracer::Scope::MC_EVACUATE_NEW_SPACE);
|
| EvacuationScope evacuation_scope(this);
|
| -
|
| EvacuateNewSpacePrologue();
|
| + local_pretenuring_feedback = EvacuateNewSpaceInParallel();
|
| + heap_->new_space()->set_age_mark(heap_->new_space()->top());
|
| + }
|
| +
|
| + {
|
| + GCTracer::Scope gc_scope(heap()->tracer(),
|
| + GCTracer::Scope::MC_EVACUATE_CANDIDATES);
|
| + EvacuationScope evacuation_scope(this);
|
| EvacuatePagesInParallel();
|
| - EvacuateNewSpaceEpilogue();
|
| - heap()->new_space()->set_age_mark(heap()->new_space()->top());
|
| + }
|
| +
|
| + {
|
| + heap_->MergeAllocationSitePretenuringFeedback(*local_pretenuring_feedback);
|
| + delete local_pretenuring_feedback;
|
| }
|
|
|
| UpdatePointersAfterEvacuation();
|
| @@ -3732,11 +3671,13 @@
|
| heap_->store_buffer()->IteratePointersToNewSpace(&UpdatePointer);
|
| }
|
|
|
| + int npages = evacuation_candidates_.length();
|
| {
|
| GCTracer::Scope gc_scope(
|
| heap()->tracer(),
|
| GCTracer::Scope::MC_EVACUATE_UPDATE_POINTERS_BETWEEN_EVACUATED);
|
| - for (Page* p : evacuation_candidates_) {
|
| + for (int i = 0; i < npages; i++) {
|
| + Page* p = evacuation_candidates_[i];
|
| DCHECK(p->IsEvacuationCandidate() ||
|
| p->IsFlagSet(Page::RESCAN_ON_EVACUATION));
|
|
|
| @@ -3811,7 +3752,9 @@
|
|
|
|
|
| void MarkCompactCollector::ReleaseEvacuationCandidates() {
|
| - for (Page* p : evacuation_candidates_) {
|
| + int npages = evacuation_candidates_.length();
|
| + for (int i = 0; i < npages; i++) {
|
| + Page* p = evacuation_candidates_[i];
|
| if (!p->IsEvacuationCandidate()) continue;
|
| PagedSpace* space = static_cast<PagedSpace*>(p->owner());
|
| space->Free(p->area_start(), p->area_size());
|
|
|