[heap] Run cleaning of string table in parallel to other cleaning

Bug: v8:12813
Change-Id: I27bbf5190165a0d919f021bbcf089e203dfed83f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3592955
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80072}
This commit is contained in:
Michael Lippautz 2022-04-20 18:49:09 +02:00 committed by V8 LUCI CQ
parent d0c147ab10
commit 7a96ceccb8
3 changed files with 102 additions and 32 deletions

View File

@ -908,13 +908,14 @@ void GCTracer::PrintNVP() const {
"heap.external.epilogue=%.1f " "heap.external.epilogue=%.1f "
"heap.external.weak_global_handles=%.1f " "heap.external.weak_global_handles=%.1f "
"clear=%1.f " "clear=%1.f "
"clear.external_string_table=%.1f "
"clear.dependent_code=%.1f " "clear.dependent_code=%.1f "
"clear.maps=%.1f " "clear.maps=%.1f "
"clear.slots_buffer=%.1f " "clear.slots_buffer=%.1f "
"clear.string_table=%.1f "
"clear.weak_collections=%.1f " "clear.weak_collections=%.1f "
"clear.weak_lists=%.1f " "clear.weak_lists=%.1f "
"clear.weak_references=%.1f " "clear.weak_references=%.1f "
"clear.join_job=%.1f "
"complete.sweep_array_buffers=%.1f " "complete.sweep_array_buffers=%.1f "
"epilogue=%.1f " "epilogue=%.1f "
"evacuate=%.1f " "evacuate=%.1f "
@ -998,13 +999,14 @@ void GCTracer::PrintNVP() const {
current_scope(Scope::HEAP_EXTERNAL_EPILOGUE), current_scope(Scope::HEAP_EXTERNAL_EPILOGUE),
current_scope(Scope::HEAP_EXTERNAL_WEAK_GLOBAL_HANDLES), current_scope(Scope::HEAP_EXTERNAL_WEAK_GLOBAL_HANDLES),
current_scope(Scope::MC_CLEAR), current_scope(Scope::MC_CLEAR),
current_scope(Scope::MC_CLEAR_EXTERNAL_STRING_TABLE),
current_scope(Scope::MC_CLEAR_DEPENDENT_CODE), current_scope(Scope::MC_CLEAR_DEPENDENT_CODE),
current_scope(Scope::MC_CLEAR_MAPS), current_scope(Scope::MC_CLEAR_MAPS),
current_scope(Scope::MC_CLEAR_SLOTS_BUFFER), current_scope(Scope::MC_CLEAR_SLOTS_BUFFER),
current_scope(Scope::MC_CLEAR_STRING_TABLE),
current_scope(Scope::MC_CLEAR_WEAK_COLLECTIONS), current_scope(Scope::MC_CLEAR_WEAK_COLLECTIONS),
current_scope(Scope::MC_CLEAR_WEAK_LISTS), current_scope(Scope::MC_CLEAR_WEAK_LISTS),
current_scope(Scope::MC_CLEAR_WEAK_REFERENCES), current_scope(Scope::MC_CLEAR_WEAK_REFERENCES),
current_scope(Scope::MC_CLEAR_JOIN_JOB),
current_scope(Scope::MC_COMPLETE_SWEEP_ARRAY_BUFFERS), current_scope(Scope::MC_COMPLETE_SWEEP_ARRAY_BUFFERS),
current_scope(Scope::MC_EPILOGUE), current_scope(Scope::MC_EVACUATE), current_scope(Scope::MC_EPILOGUE), current_scope(Scope::MC_EVACUATE),
current_scope(Scope::MC_EVACUATE_CANDIDATES), current_scope(Scope::MC_EVACUATE_CANDIDATES),

View File

@ -4,6 +4,7 @@
#include "src/heap/mark-compact.h" #include "src/heap/mark-compact.h"
#include <memory>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
@ -1314,10 +1315,9 @@ class MarkCompactCollector::SharedHeapObjectVisitor final
MarkCompactCollector* const collector_; MarkCompactCollector* const collector_;
}; };
class InternalizedStringTableCleaner : public RootVisitor { class InternalizedStringTableCleaner final : public RootVisitor {
public: public:
explicit InternalizedStringTableCleaner(Heap* heap) explicit InternalizedStringTableCleaner(Heap* heap) : heap_(heap) {}
: heap_(heap), pointers_removed_(0) {}
void VisitRootPointers(Root root, const char* description, void VisitRootPointers(Root root, const char* description,
FullObjectSlot start, FullObjectSlot end) override { FullObjectSlot start, FullObjectSlot end) override {
@ -1329,8 +1329,7 @@ class InternalizedStringTableCleaner : public RootVisitor {
OffHeapObjectSlot end) override { OffHeapObjectSlot end) override {
DCHECK_EQ(root, Root::kStringTable); DCHECK_EQ(root, Root::kStringTable);
// Visit all HeapObject pointers in [start, end). // Visit all HeapObject pointers in [start, end).
MarkCompactCollector::NonAtomicMarkingState* marking_state = auto* marking_state = heap_->mark_compact_collector()->marking_state();
heap_->mark_compact_collector()->non_atomic_marking_state();
Isolate* isolate = heap_->isolate(); Isolate* isolate = heap_->isolate();
for (OffHeapObjectSlot p = start; p < end; ++p) { for (OffHeapObjectSlot p = start; p < end; ++p) {
Object o = p.load(isolate); Object o = p.load(isolate);
@ -1346,11 +1345,11 @@ class InternalizedStringTableCleaner : public RootVisitor {
} }
} }
int PointersRemoved() { return pointers_removed_; } int PointersRemoved() const { return pointers_removed_; }
private: private:
Heap* heap_; Heap* heap_;
int pointers_removed_; int pointers_removed_ = 0;
}; };
class ExternalStringTableCleaner : public RootVisitor { class ExternalStringTableCleaner : public RootVisitor {
@ -1390,7 +1389,7 @@ class ExternalStringTableCleaner : public RootVisitor {
class MarkCompactWeakObjectRetainer : public WeakObjectRetainer { class MarkCompactWeakObjectRetainer : public WeakObjectRetainer {
public: public:
explicit MarkCompactWeakObjectRetainer( explicit MarkCompactWeakObjectRetainer(
MarkCompactCollector::NonAtomicMarkingState* marking_state) MarkCompactCollector::MarkingState* marking_state)
: marking_state_(marking_state) {} : marking_state_(marking_state) {}
Object RetainAs(Object object) override { Object RetainAs(Object object) override {
@ -1420,7 +1419,7 @@ class MarkCompactWeakObjectRetainer : public WeakObjectRetainer {
} }
private: private:
MarkCompactCollector::NonAtomicMarkingState* marking_state_; MarkCompactCollector::MarkingState* const marking_state_;
}; };
class RecordMigratedSlotVisitor : public ObjectVisitorWithCageBases { class RecordMigratedSlotVisitor : public ObjectVisitorWithCageBases {
@ -2495,31 +2494,94 @@ void MarkCompactCollector::MarkLiveObjects() {
epoch_++; epoch_++;
} }
namespace {
class ParallelClearingJob final : public v8::JobTask {
public:
class ClearingItem {
public:
virtual ~ClearingItem() = default;
virtual void Run(JobDelegate* delegate) = 0;
};
ParallelClearingJob() = default;
~ParallelClearingJob() override = default;
ParallelClearingJob(const ParallelClearingJob&) = delete;
ParallelClearingJob& operator=(const ParallelClearingJob&) = delete;
// v8::JobTask overrides.
void Run(JobDelegate* delegate) override {
std::unique_ptr<ClearingItem> item;
{
base::MutexGuard guard(&items_mutex_);
item = std::move(items_.back());
items_.pop_back();
}
item->Run(delegate);
}
size_t GetMaxConcurrency(size_t worker_count) const override {
base::MutexGuard guard(&items_mutex_);
return items_.size();
}
void Add(std::unique_ptr<ClearingItem> item) {
items_.push_back(std::move(item));
}
private:
mutable base::Mutex items_mutex_;
std::vector<std::unique_ptr<ClearingItem>> items_;
};
class ClearStringTableJobItem final : public ParallelClearingJob::ClearingItem {
public:
explicit ClearStringTableJobItem(Isolate* isolate) : isolate_(isolate) {}
void Run(JobDelegate* delegate) final {
if (isolate_->OwnsStringTable()) {
TRACE_GC1(isolate_->heap()->tracer(),
GCTracer::Scope::MC_CLEAR_STRING_TABLE,
delegate->IsJoiningThread() ? ThreadKind::kMain
: ThreadKind::kBackground);
// Prune the string table removing all strings only pointed to by the
// string table. Cannot use string_table() here because the string
// table is marked.
StringTable* string_table = isolate_->string_table();
InternalizedStringTableCleaner internalized_visitor(isolate_->heap());
string_table->DropOldData();
string_table->IterateElements(&internalized_visitor);
string_table->NotifyElementsRemoved(
internalized_visitor.PointersRemoved());
}
}
private:
Isolate* const isolate_;
};
} // namespace
void MarkCompactCollector::ClearNonLiveReferences() { void MarkCompactCollector::ClearNonLiveReferences() {
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR); TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR);
if (isolate()->OwnsStringTable()) { auto clearing_job = std::make_unique<ParallelClearingJob>();
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR_STRING_TABLE); clearing_job->Add(std::make_unique<ClearStringTableJobItem>(isolate()));
auto clearing_job_handle = V8::GetCurrentPlatform()->PostJob(
TaskPriority::kUserBlocking, std::move(clearing_job));
// Prune the string table removing all strings only pointed to by the {
// string table. Cannot use string_table() here because the string TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR_EXTERNAL_STRING_TABLE);
// table is marked. ExternalStringTableCleaner external_visitor(heap());
StringTable* string_table = isolate()->string_table(); heap()->external_string_table_.IterateAll(&external_visitor);
InternalizedStringTableCleaner internalized_visitor(heap()); heap()->external_string_table_.CleanUpAll();
string_table->DropOldData();
string_table->IterateElements(&internalized_visitor);
string_table->NotifyElementsRemoved(internalized_visitor.PointersRemoved());
} }
ExternalStringTableCleaner external_visitor(heap());
heap()->external_string_table_.IterateAll(&external_visitor);
heap()->external_string_table_.CleanUpAll();
{ {
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR_FLUSHABLE_BYTECODE); TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR_FLUSHABLE_BYTECODE);
// ProcessFlusheBaselineCandidates should be called after clearing bytecode // `ProcessFlusheBaselineCandidates()` must be called after
// so that we flush any bytecode if needed so we could correctly set the // `ProcessOldCodeCandidates()` so that we correctly set the code object on
// code object on the JSFunction. // the JSFunction after flushing.
ProcessOldCodeCandidates(); ProcessOldCodeCandidates();
ProcessFlushedBaselineCandidates(); ProcessFlushedBaselineCandidates();
} }
@ -2532,8 +2594,7 @@ void MarkCompactCollector::ClearNonLiveReferences() {
{ {
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR_WEAK_LISTS); TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR_WEAK_LISTS);
// Process the weak references. // Process the weak references.
MarkCompactWeakObjectRetainer mark_compact_object_retainer( MarkCompactWeakObjectRetainer mark_compact_object_retainer(marking_state());
non_atomic_marking_state());
heap()->ProcessAllWeakReferences(&mark_compact_object_retainer); heap()->ProcessAllWeakReferences(&mark_compact_object_retainer);
} }
@ -2562,6 +2623,11 @@ void MarkCompactCollector::ClearNonLiveReferences() {
} }
#endif // V8_SANDBOXED_EXTERNAL_POINTERS #endif // V8_SANDBOXED_EXTERNAL_POINTERS
{
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR_JOIN_JOB);
clearing_job_handle->Join();
}
DCHECK(weak_objects_.transition_arrays.IsEmpty()); DCHECK(weak_objects_.transition_arrays.IsEmpty());
DCHECK(weak_objects_.weak_references.IsEmpty()); DCHECK(weak_objects_.weak_references.IsEmpty());
DCHECK(weak_objects_.weak_objects_in_code.IsEmpty()); DCHECK(weak_objects_.weak_objects_in_code.IsEmpty());
@ -2676,8 +2742,8 @@ void MarkCompactCollector::FlushBytecodeFromSFI(
// Mark the uncompiled data as black, and ensure all fields have already been // Mark the uncompiled data as black, and ensure all fields have already been
// marked. // marked.
DCHECK(non_atomic_marking_state()->IsBlackOrGrey(inferred_name)); DCHECK(marking_state()->IsBlackOrGrey(inferred_name));
non_atomic_marking_state()->WhiteToBlack(uncompiled_data); marking_state()->WhiteToBlack(uncompiled_data);
// Use the raw function data setter to avoid validity checks, since we're // Use the raw function data setter to avoid validity checks, since we're
// performing the unusual task of decompiling. // performing the unusual task of decompiling.

View File

@ -535,8 +535,10 @@
F(MARK_COMPACTOR) \ F(MARK_COMPACTOR) \
TOP_MC_SCOPES(F) \ TOP_MC_SCOPES(F) \
F(MC_CLEAR_DEPENDENT_CODE) \ F(MC_CLEAR_DEPENDENT_CODE) \
F(MC_CLEAR_EXTERNAL_STRING_TABLE) \
F(MC_CLEAR_FLUSHABLE_BYTECODE) \ F(MC_CLEAR_FLUSHABLE_BYTECODE) \
F(MC_CLEAR_FLUSHED_JS_FUNCTIONS) \ F(MC_CLEAR_FLUSHED_JS_FUNCTIONS) \
F(MC_CLEAR_JOIN_JOB) \
F(MC_CLEAR_MAPS) \ F(MC_CLEAR_MAPS) \
F(MC_CLEAR_SLOTS_BUFFER) \ F(MC_CLEAR_SLOTS_BUFFER) \
F(MC_CLEAR_STRING_TABLE) \ F(MC_CLEAR_STRING_TABLE) \