Commit a3f66927 authored by Michael Lippautz's avatar Michael Lippautz Committed by V8 LUCI CQ

[heap] Refactor atomic marking phase

The atomic marking phase was organized in many distinct smaller
phases. In particular, before http://crrev.com/c/3584115 the marking
phase split into two large separate phases.

This CL reorganizes marking into two phases that perform regular V8
heap marking, Oilpan, and ephemerons:
- A parallel phase that likely drains all marking worklists;
- A single-threaded final phase to catch any left overs;

This avoids artificial splitting in phases and also avoids repeated
starting and joining of jobs.

Change-Id: I5cccfc5777837d9ece10d8f4925781bf2d07d9da
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3602507Reviewed-by: 's avatarOmer Katz <omerkatz@chromium.org>
Reviewed-by: 's avatarDominik Inführ <dinfuehr@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80265}
parent 692aeb27
...@@ -196,6 +196,8 @@ void Worklist<EntryType, SegmentSize>::Merge( ...@@ -196,6 +196,8 @@ void Worklist<EntryType, SegmentSize>::Merge(
template <typename EntryType, uint16_t SegmentSize> template <typename EntryType, uint16_t SegmentSize>
void Worklist<EntryType, SegmentSize>::Swap( void Worklist<EntryType, SegmentSize>::Swap(
Worklist<EntryType, SegmentSize>* other) { Worklist<EntryType, SegmentSize>* other) {
v8::base::MutexGuard guard1(&lock_);
v8::base::MutexGuard guard2(&other->lock_);
Segment* top = top_; Segment* top = top_;
set_top(other->top_); set_top(other->top_);
other->set_top(top); other->set_top(top);
......
...@@ -604,6 +604,10 @@ bool CppHeap::AdvanceTracing(double max_duration) { ...@@ -604,6 +604,10 @@ bool CppHeap::AdvanceTracing(double max_duration) {
: v8::base::TimeDelta::FromMillisecondsD(max_duration); : v8::base::TimeDelta::FromMillisecondsD(max_duration);
const size_t marked_bytes_limit = in_atomic_pause_ ? SIZE_MAX : 0; const size_t marked_bytes_limit = in_atomic_pause_ ? SIZE_MAX : 0;
DCHECK_NOT_NULL(marker_); DCHECK_NOT_NULL(marker_);
if (in_atomic_pause_) {
marker_->NotifyConcurrentMarkingOfWorkIfNeeded(
cppgc::TaskPriority::kUserBlocking);
}
// TODO(chromium:1056170): Replace when unified heap transitions to // TODO(chromium:1056170): Replace when unified heap transitions to
// bytes-based deadline. // bytes-based deadline.
marking_done_ = marking_done_ =
......
...@@ -238,6 +238,13 @@ void ConcurrentMarkerBase::NotifyIncrementalMutatorStepCompleted() { ...@@ -238,6 +238,13 @@ void ConcurrentMarkerBase::NotifyIncrementalMutatorStepCompleted() {
} }
} }
void ConcurrentMarkerBase::NotifyOfWorkIfNeeded(cppgc::TaskPriority priority) {
if (HasWorkForConcurrentMarking(marking_worklists_)) {
concurrent_marking_handle_->UpdatePriority(priority);
concurrent_marking_handle_->NotifyConcurrencyIncrease();
}
}
void ConcurrentMarkerBase::IncreaseMarkingPriorityIfNeeded() { void ConcurrentMarkerBase::IncreaseMarkingPriorityIfNeeded() {
if (!concurrent_marking_handle_->UpdatePriorityEnabled()) return; if (!concurrent_marking_handle_->UpdatePriorityEnabled()) return;
if (concurrent_marking_priority_increased_) return; if (concurrent_marking_priority_increased_) return;
......
...@@ -30,6 +30,7 @@ class V8_EXPORT_PRIVATE ConcurrentMarkerBase { ...@@ -30,6 +30,7 @@ class V8_EXPORT_PRIVATE ConcurrentMarkerBase {
bool Cancel(); bool Cancel();
void NotifyIncrementalMutatorStepCompleted(); void NotifyIncrementalMutatorStepCompleted();
void NotifyOfWorkIfNeeded(cppgc::TaskPriority priority);
bool IsActive() const; bool IsActive() const;
......
...@@ -462,6 +462,13 @@ bool MarkerBase::JoinConcurrentMarkingIfNeeded() { ...@@ -462,6 +462,13 @@ bool MarkerBase::JoinConcurrentMarkingIfNeeded() {
return true; return true;
} }
void MarkerBase::NotifyConcurrentMarkingOfWorkIfNeeded(
cppgc::TaskPriority priority) {
if (concurrent_marker_->IsActive()) {
concurrent_marker_->NotifyOfWorkIfNeeded(priority);
}
}
bool MarkerBase::AdvanceMarkingWithLimits(v8::base::TimeDelta max_duration, bool MarkerBase::AdvanceMarkingWithLimits(v8::base::TimeDelta max_duration,
size_t marked_bytes_limit) { size_t marked_bytes_limit) {
bool is_done = false; bool is_done = false;
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <memory> #include <memory>
#include "include/cppgc/heap.h" #include "include/cppgc/heap.h"
#include "include/cppgc/platform.h"
#include "include/cppgc/visitor.h" #include "include/cppgc/visitor.h"
#include "src/base/macros.h" #include "src/base/macros.h"
#include "src/base/platform/time.h" #include "src/base/platform/time.h"
...@@ -112,6 +113,7 @@ class V8_EXPORT_PRIVATE MarkerBase { ...@@ -112,6 +113,7 @@ class V8_EXPORT_PRIVATE MarkerBase {
void ProcessWeakness(); void ProcessWeakness();
bool JoinConcurrentMarkingIfNeeded(); bool JoinConcurrentMarkingIfNeeded();
void NotifyConcurrentMarkingOfWorkIfNeeded(cppgc::TaskPriority);
inline void WriteBarrierForInConstructionObject(HeapObjectHeader&); inline void WriteBarrierForInConstructionObject(HeapObjectHeader&);
......
...@@ -908,11 +908,10 @@ void GCTracer::PrintNVP() const { ...@@ -908,11 +908,10 @@ void GCTracer::PrintNVP() const {
"mark=%.1f " "mark=%.1f "
"mark.finish_incremental=%.1f " "mark.finish_incremental=%.1f "
"mark.roots=%.1f " "mark.roots=%.1f "
"mark.main=%.1f " "mark.full_closure_parallel=%.1f "
"mark.weak_closure=%.1f " "mark.full_closure=%.1f "
"mark.weak_closure.ephemeron=%.1f " "mark.ephemeron.marking=%.1f "
"mark.weak_closure.ephemeron.marking=%.1f " "mark.ephemeron.linear=%.1f "
"mark.weak_closure.ephemeron.linear=%.1f "
"mark.embedder_prologue=%.1f " "mark.embedder_prologue=%.1f "
"mark.embedder_tracing=%.1f " "mark.embedder_tracing=%.1f "
"prologue=%.1f " "prologue=%.1f "
...@@ -996,9 +995,8 @@ void GCTracer::PrintNVP() const { ...@@ -996,9 +995,8 @@ void GCTracer::PrintNVP() const {
current_scope(Scope::MC_MARK), current_scope(Scope::MC_MARK),
current_scope(Scope::MC_MARK_FINISH_INCREMENTAL), current_scope(Scope::MC_MARK_FINISH_INCREMENTAL),
current_scope(Scope::MC_MARK_ROOTS), current_scope(Scope::MC_MARK_ROOTS),
current_scope(Scope::MC_MARK_MAIN), current_scope(Scope::MC_MARK_FULL_CLOSURE_PARALLEL),
current_scope(Scope::MC_MARK_WEAK_CLOSURE), current_scope(Scope::MC_MARK_FULL_CLOSURE),
current_scope(Scope::MC_MARK_WEAK_CLOSURE_EPHEMERON),
current_scope(Scope::MC_MARK_WEAK_CLOSURE_EPHEMERON_MARKING), current_scope(Scope::MC_MARK_WEAK_CLOSURE_EPHEMERON_MARKING),
current_scope(Scope::MC_MARK_WEAK_CLOSURE_EPHEMERON_LINEAR), current_scope(Scope::MC_MARK_WEAK_CLOSURE_EPHEMERON_LINEAR),
current_scope(Scope::MC_MARK_EMBEDDER_PROLOGUE), current_scope(Scope::MC_MARK_EMBEDDER_PROLOGUE),
......
...@@ -2018,7 +2018,7 @@ void MarkCompactCollector::RevisitObject(HeapObject obj) { ...@@ -2018,7 +2018,7 @@ void MarkCompactCollector::RevisitObject(HeapObject obj) {
marking_visitor_->Visit(obj.map(marking_visitor_->cage_base()), obj); marking_visitor_->Visit(obj.map(marking_visitor_->cage_base()), obj);
} }
bool MarkCompactCollector::ProcessEphemeronsUntilFixpoint() { bool MarkCompactCollector::MarkTransitiveClosureUntilFixpoint() {
int iterations = 0; int iterations = 0;
int max_iterations = FLAG_ephemeron_fixpoint_iterations; int max_iterations = FLAG_ephemeron_fixpoint_iterations;
...@@ -2042,14 +2042,7 @@ bool MarkCompactCollector::ProcessEphemeronsUntilFixpoint() { ...@@ -2042,14 +2042,7 @@ bool MarkCompactCollector::ProcessEphemeronsUntilFixpoint() {
{ {
TRACE_GC(heap()->tracer(), TRACE_GC(heap()->tracer(),
GCTracer::Scope::MC_MARK_WEAK_CLOSURE_EPHEMERON_MARKING); GCTracer::Scope::MC_MARK_WEAK_CLOSURE_EPHEMERON_MARKING);
if (FLAG_parallel_marking) {
heap_->concurrent_marking()->RescheduleJobIfNeeded(
TaskPriority::kUserBlocking);
}
another_ephemeron_iteration_main_thread = ProcessEphemerons(); another_ephemeron_iteration_main_thread = ProcessEphemerons();
FinishConcurrentMarking();
} }
CHECK( CHECK(
...@@ -2064,10 +2057,6 @@ bool MarkCompactCollector::ProcessEphemeronsUntilFixpoint() { ...@@ -2064,10 +2057,6 @@ bool MarkCompactCollector::ProcessEphemeronsUntilFixpoint() {
!local_marking_worklists()->IsWrapperEmpty() || !local_marking_worklists()->IsWrapperEmpty() ||
!heap()->local_embedder_heap_tracer()->IsRemoteTracingDone()); !heap()->local_embedder_heap_tracer()->IsRemoteTracingDone());
CHECK(local_marking_worklists()->IsEmpty());
CHECK(local_weak_objects()->current_ephemerons_local.IsLocalAndGlobalEmpty());
CHECK(local_weak_objects()
->discovered_ephemerons_local.IsLocalAndGlobalEmpty());
return true; return true;
} }
...@@ -2109,7 +2098,7 @@ bool MarkCompactCollector::ProcessEphemerons() { ...@@ -2109,7 +2098,7 @@ bool MarkCompactCollector::ProcessEphemerons() {
return another_ephemeron_iteration; return another_ephemeron_iteration;
} }
void MarkCompactCollector::ProcessEphemeronsLinear() { void MarkCompactCollector::MarkTransitiveClosureLinear() {
TRACE_GC(heap()->tracer(), TRACE_GC(heap()->tracer(),
GCTracer::Scope::MC_MARK_WEAK_CLOSURE_EPHEMERON_LINEAR); GCTracer::Scope::MC_MARK_WEAK_CLOSURE_EPHEMERON_LINEAR);
CHECK(heap()->concurrent_marking()->IsStopped()); CHECK(heap()->concurrent_marking()->IsStopped());
...@@ -2233,6 +2222,9 @@ std::pair<size_t, size_t> MarkCompactCollector::ProcessMarkingWorklist( ...@@ -2233,6 +2222,9 @@ std::pair<size_t, size_t> MarkCompactCollector::ProcessMarkingWorklist(
PtrComprCageBase cage_base(isolate); PtrComprCageBase cage_base(isolate);
CodePageHeaderModificationScope rwx_write_scope( CodePageHeaderModificationScope rwx_write_scope(
"Marking of Code objects require write access to Code page headers"); "Marking of Code objects require write access to Code page headers");
if (parallel_marking_)
heap_->concurrent_marking()->RescheduleJobIfNeeded(
TaskPriority::kUserBlocking);
while (local_marking_worklists()->Pop(&object) || while (local_marking_worklists()->Pop(&object) ||
local_marking_worklists()->PopOnHold(&object)) { local_marking_worklists()->PopOnHold(&object)) {
// Left trimming may result in grey or black filler objects on the marking // Left trimming may result in grey or black filler objects on the marking
...@@ -2299,19 +2291,7 @@ bool MarkCompactCollector::ProcessEphemeron(HeapObject key, HeapObject value) { ...@@ -2299,19 +2291,7 @@ bool MarkCompactCollector::ProcessEphemeron(HeapObject key, HeapObject value) {
return false; return false;
} }
void MarkCompactCollector::ProcessEphemeronMarking() { void MarkCompactCollector::VerifyEphemeronMarking() {
DCHECK(local_marking_worklists()->IsEmpty());
// Incremental marking might leave ephemerons in main task's local
// buffer, flush it into global pool.
local_weak_objects()->next_ephemerons_local.Publish();
if (!ProcessEphemeronsUntilFixpoint()) {
// Fixpoint iteration needed too many iterations and was cancelled. Use the
// guaranteed linear algorithm.
ProcessEphemeronsLinear();
}
#ifdef VERIFY_HEAP #ifdef VERIFY_HEAP
if (FLAG_verify_heap) { if (FLAG_verify_heap) {
Ephemeron ephemeron; Ephemeron ephemeron;
...@@ -2323,10 +2303,19 @@ void MarkCompactCollector::ProcessEphemeronMarking() { ...@@ -2323,10 +2303,19 @@ void MarkCompactCollector::ProcessEphemeronMarking() {
CHECK(!ProcessEphemeron(ephemeron.key, ephemeron.value)); CHECK(!ProcessEphemeron(ephemeron.key, ephemeron.value));
} }
} }
#endif #endif // VERIFY_HEAP
}
CHECK(local_marking_worklists()->IsEmpty()); void MarkCompactCollector::MarkTransitiveClosure() {
CHECK(heap()->local_embedder_heap_tracer()->IsRemoteTracingDone()); // Incremental marking might leave ephemerons in main task's local
// buffer, flush it into global pool.
local_weak_objects()->next_ephemerons_local.Publish();
if (!MarkTransitiveClosureUntilFixpoint()) {
// Fixpoint iteration needed too many iterations and was cancelled. Use the
// guaranteed linear algorithm.
MarkTransitiveClosureLinear();
}
} }
void MarkCompactCollector::ProcessTopOptimizedFrame(ObjectVisitor* visitor, void MarkCompactCollector::ProcessTopOptimizedFrame(ObjectVisitor* visitor,
...@@ -2407,50 +2396,32 @@ void MarkCompactCollector::MarkLiveObjects() { ...@@ -2407,50 +2396,32 @@ void MarkCompactCollector::MarkLiveObjects() {
MarkObjectsFromClientHeaps(); MarkObjectsFromClientHeaps();
} }
{ if (FLAG_parallel_marking) {
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_MAIN); TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_FULL_CLOSURE_PARALLEL);
if (FLAG_parallel_marking) { parallel_marking_ = true;
heap_->concurrent_marking()->RescheduleJobIfNeeded( heap_->concurrent_marking()->RescheduleJobIfNeeded(
TaskPriority::kUserBlocking); TaskPriority::kUserBlocking);
} MarkTransitiveClosure();
DrainMarkingWorklist();
FinishConcurrentMarking();
DrainMarkingWorklist();
}
{
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_WEAK_CLOSURE);
DCHECK(local_marking_worklists()->IsEmpty());
// Mark objects reachable through the embedder heap. This phase is
// opportunistic as it may not discover graphs that are only reachable
// through ephemerons.
{ {
TRACE_GC(heap()->tracer(), TRACE_GC(heap()->tracer(),
GCTracer::Scope::MC_MARK_EMBEDDER_TRACING_CLOSURE); GCTracer::Scope::MC_MARK_FULL_CLOSURE_PARALLEL_JOIN);
do { FinishConcurrentMarking();
// PerformWrapperTracing() also empties the work items collected by
// concurrent markers. As a result this call needs to happen at least
// once.
PerformWrapperTracing();
DrainMarkingWorklist();
} while (!heap_->local_embedder_heap_tracer()->IsRemoteTracingDone() ||
!local_marking_worklists()->IsWrapperEmpty());
DCHECK(local_marking_worklists()->IsWrapperEmpty());
DCHECK(local_marking_worklists()->IsEmpty());
} }
parallel_marking_ = false;
}
// The objects reachable from the roots are marked, yet unreachable objects {
// are unmarked. Mark objects reachable due to embedder heap tracing or TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_FULL_CLOSURE);
// harmony weak maps. // Complete the transitive closure single-threaded to avoid races with
{ // multiple threads when processing weak maps and embedder heaps.
TRACE_GC(heap()->tracer(), MarkTransitiveClosure();
GCTracer::Scope::MC_MARK_WEAK_CLOSURE_EPHEMERON); CHECK(local_marking_worklists()->IsEmpty());
ProcessEphemeronMarking(); CHECK(
DCHECK(local_marking_worklists()->IsEmpty()); local_weak_objects()->current_ephemerons_local.IsLocalAndGlobalEmpty());
} CHECK(local_weak_objects()
->discovered_ephemerons_local.IsLocalAndGlobalEmpty());
CHECK(heap()->local_embedder_heap_tracer()->IsRemoteTracingDone());
VerifyEphemeronMarking();
} }
if (was_marked_incrementally) { if (was_marked_incrementally) {
......
...@@ -613,7 +613,8 @@ class MarkCompactCollector final { ...@@ -613,7 +613,8 @@ class MarkCompactCollector final {
void UpdatePointersInClientHeap(Isolate* client); void UpdatePointersInClientHeap(Isolate* client);
// Marks object reachable from harmony weak maps and wrapper tracing. // Marks object reachable from harmony weak maps and wrapper tracing.
void ProcessEphemeronMarking(); void MarkTransitiveClosure();
void VerifyEphemeronMarking();
// If the call-site of the top optimized code was not prepared for // If the call-site of the top optimized code was not prepared for
// deoptimization, then treat embedded pointers in the code as strong as // deoptimization, then treat embedded pointers in the code as strong as
...@@ -628,19 +629,21 @@ class MarkCompactCollector final { ...@@ -628,19 +629,21 @@ class MarkCompactCollector final {
// Returns true if value was actually marked. // Returns true if value was actually marked.
bool ProcessEphemeron(HeapObject key, HeapObject value); bool ProcessEphemeron(HeapObject key, HeapObject value);
// Marks ephemerons and drains marking worklist iteratively // Marks the transitive closure by draining the marking worklist iteratively,
// until a fixpoint is reached. Returns false if too many iterations have been // applying ephemerons semantics and invoking embedder tracing until a
// tried and the linear approach should be used. // fixpoint is reached. Returns false if too many iterations have been tried
bool ProcessEphemeronsUntilFixpoint(); // and the linear approach should be used.
bool MarkTransitiveClosureUntilFixpoint();
// Marks the transitive closure applying ephemeron semantics and invoking
// embedder tracing with a linear algorithm for ephemerons. Only used if
// fixpoint iteration doesn't finish within a few iterations.
void MarkTransitiveClosureLinear();
// Drains ephemeron and marking worklists. Single iteration of the // Drains ephemeron and marking worklists. Single iteration of the
// fixpoint iteration. // fixpoint iteration.
bool ProcessEphemerons(); bool ProcessEphemerons();
// Mark ephemerons and drain marking worklist with a linear algorithm.
// Only used if fixpoint iteration doesn't finish within a few iterations.
void ProcessEphemeronsLinear();
// Perform Wrapper Tracing if in use. // Perform Wrapper Tracing if in use.
void PerformWrapperTracing(); void PerformWrapperTracing();
...@@ -745,6 +748,7 @@ class MarkCompactCollector final { ...@@ -745,6 +748,7 @@ class MarkCompactCollector final {
bool compacting_ = false; bool compacting_ = false;
bool black_allocation_ = false; bool black_allocation_ = false;
bool have_code_to_deoptimize_ = false; bool have_code_to_deoptimize_ = false;
bool parallel_marking_ = false;
MarkingWorklists marking_worklists_; MarkingWorklists marking_worklists_;
......
...@@ -42,7 +42,7 @@ class TransitionArray; ...@@ -42,7 +42,7 @@ class TransitionArray;
/* Keep track of all ephemerons for concurrent marking tasks. Only store \ /* Keep track of all ephemerons for concurrent marking tasks. Only store \
ephemerons in these worklists if both (key, value) are unreachable at \ ephemerons in these worklists if both (key, value) are unreachable at \
the moment. \ the moment. \
MarkCompactCollector::ProcessEphemeronsUntilFixpoint drains/fills \ MarkCompactCollector::MarkTransitiveClosureUntilFixpoint drains/fills \
these worklists. current_ephemerons is used as draining worklist in \ these worklists. current_ephemerons is used as draining worklist in \
the current fixpoint iteration. */ \ the current fixpoint iteration. */ \
F(Ephemeron, current_ephemerons, CurrentEphemerons) \ F(Ephemeron, current_ephemerons, CurrentEphemerons) \
......
...@@ -566,12 +566,11 @@ ...@@ -566,12 +566,11 @@
F(MC_MARK_CLIENT_HEAPS) \ F(MC_MARK_CLIENT_HEAPS) \
F(MC_MARK_EMBEDDER_PROLOGUE) \ F(MC_MARK_EMBEDDER_PROLOGUE) \
F(MC_MARK_EMBEDDER_TRACING) \ F(MC_MARK_EMBEDDER_TRACING) \
F(MC_MARK_EMBEDDER_TRACING_CLOSURE) \
F(MC_MARK_FINISH_INCREMENTAL) \ F(MC_MARK_FINISH_INCREMENTAL) \
F(MC_MARK_MAIN) \ F(MC_MARK_FULL_CLOSURE_PARALLEL) \
F(MC_MARK_FULL_CLOSURE_PARALLEL_JOIN) \
F(MC_MARK_ROOTS) \ F(MC_MARK_ROOTS) \
F(MC_MARK_WEAK_CLOSURE) \ F(MC_MARK_FULL_CLOSURE) \
F(MC_MARK_WEAK_CLOSURE_EPHEMERON) \
F(MC_MARK_WEAK_CLOSURE_EPHEMERON_MARKING) \ F(MC_MARK_WEAK_CLOSURE_EPHEMERON_MARKING) \
F(MC_MARK_WEAK_CLOSURE_EPHEMERON_LINEAR) \ F(MC_MARK_WEAK_CLOSURE_EPHEMERON_LINEAR) \
F(MC_SWEEP_CODE) \ F(MC_SWEEP_CODE) \
......
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