cppgc: Return empty pages right away when sweeping on allocation

This should save about 0.23% cycles on the JQuery Speedometer story.

Bug: chromium:1406296
Change-Id: Ib2b76325c6441eb5da8051c3701291abf347c56d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4152952
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Commit-Queue: Anton Bikineev <bikineev@chromium.org>
Cr-Commit-Position: refs/heads/main@{#85230}
This commit is contained in:
Anton Bikineev 2023-01-11 14:50:48 +01:00 committed by V8 LUCI CQ
parent c617c553a6
commit 78717ce93d
2 changed files with 85 additions and 15 deletions

View File

@ -413,9 +413,17 @@ class SweepFinalizer final {
using FreeMemoryHandling = SweepingConfig::FreeMemoryHandling;
public:
enum class EmptyPageHandling {
kDestroy,
kReturn,
};
SweepFinalizer(cppgc::Platform* platform,
FreeMemoryHandling free_memory_handling)
: platform_(platform), free_memory_handling_(free_memory_handling) {}
FreeMemoryHandling free_memory_handling,
EmptyPageHandling empty_page_handling_type)
: platform_(platform),
free_memory_handling_(free_memory_handling),
empty_page_handling_(empty_page_handling_type) {}
void FinalizeHeap(SpaceStates* space_states) {
for (SpaceState& space_state : *space_states) {
@ -471,8 +479,22 @@ class SweepFinalizer final {
// Unmap page if empty.
if (page_state->is_empty) {
BasePage::Destroy(page);
return;
if (empty_page_handling_ == EmptyPageHandling::kDestroy ||
page->is_large()) {
BasePage::Destroy(page);
return;
}
// Otherwise, we currently sweep on allocation. Reinitialize the empty
// page and return it right away.
auto* normal_page = NormalPage::From(page);
page_state->cached_free_list.Clear();
page_state->cached_free_list.Add(
{normal_page->PayloadStart(), normal_page->PayloadSize()});
page_state->unfinalized_free_list.clear();
page_state->largest_new_free_list_entry = normal_page->PayloadSize();
}
DCHECK(!page->is_large());
@ -482,13 +504,15 @@ class SweepFinalizer final {
space_freelist.Append(std::move(page_state->cached_free_list));
// Merge freelist with finalizers.
std::unique_ptr<FreeHandlerBase> handler =
(free_memory_handling_ == FreeMemoryHandling::kDiscardWherePossible)
? std::unique_ptr<FreeHandlerBase>(new DiscardingFreeHandler(
*platform_->GetPageAllocator(), space_freelist, *page))
: std::unique_ptr<FreeHandlerBase>(new RegularFreeHandler(
*platform_->GetPageAllocator(), space_freelist, *page));
handler->FreeFreeList(page_state->unfinalized_free_list);
if (!page_state->unfinalized_free_list.empty()) {
std::unique_ptr<FreeHandlerBase> handler =
(free_memory_handling_ == FreeMemoryHandling::kDiscardWherePossible)
? std::unique_ptr<FreeHandlerBase>(new DiscardingFreeHandler(
*platform_->GetPageAllocator(), space_freelist, *page))
: std::unique_ptr<FreeHandlerBase>(new RegularFreeHandler(
*platform_->GetPageAllocator(), space_freelist, *page));
handler->FreeFreeList(page_state->unfinalized_free_list);
}
largest_new_free_list_entry_ = std::max(
page_state->largest_new_free_list_entry, largest_new_free_list_entry_);
@ -509,6 +533,7 @@ class SweepFinalizer final {
cppgc::Platform* platform_;
size_t largest_new_free_list_entry_ = 0;
const FreeMemoryHandling free_memory_handling_;
const EmptyPageHandling empty_page_handling_;
};
class MutatorThreadSweeper final : private HeapVisitor<MutatorThreadSweeper> {
@ -544,7 +569,8 @@ class MutatorThreadSweeper final : private HeapVisitor<MutatorThreadSweeper> {
const auto deadline = v8::base::TimeTicks::Now() + max_duration;
// First, prioritize finalization of pages that were swept concurrently.
SweepFinalizer finalizer(platform_, free_memory_handling_);
SweepFinalizer finalizer(platform_, free_memory_handling_,
SweepFinalizer::EmptyPageHandling::kDestroy);
if (!finalizer.FinalizeSpaceWithDeadline(&state, deadline)) {
return false;
}
@ -831,7 +857,8 @@ class Sweeper::SweeperImpl final {
{
// First, process unfinalized pages as finalizing a page is faster than
// sweeping.
SweepFinalizer finalizer(platform_, config_.free_memory_handling);
SweepFinalizer finalizer(platform_, config_.free_memory_handling,
SweepFinalizer::EmptyPageHandling::kReturn);
while (auto page = space_state.swept_unfinalized_pages.Pop()) {
finalizer.FinalizePage(&*page);
if (size <= finalizer.largest_new_free_list_entry()) {
@ -924,7 +951,8 @@ class Sweeper::SweeperImpl final {
MutatorThreadSweepingScope sweeping_in_progress(*this);
// First, call finalizers on the mutator thread.
SweepFinalizer finalizer(platform_, config_.free_memory_handling);
SweepFinalizer finalizer(platform_, config_.free_memory_handling,
SweepFinalizer::EmptyPageHandling::kDestroy);
finalizer.FinalizeHeap(&space_states_);
// Then, help out the concurrent thread.
@ -1108,7 +1136,8 @@ class Sweeper::SweeperImpl final {
void SynchronizeAndFinalizeConcurrentSweeping() {
CancelSweepers();
SweepFinalizer finalizer(platform_, config_.free_memory_handling);
SweepFinalizer finalizer(platform_, config_.free_memory_handling,
SweepFinalizer::EmptyPageHandling::kDestroy);
finalizer.FinalizeHeap(&space_states_);
}

View File

@ -355,5 +355,46 @@ TEST_F(ConcurrentSweeperTest, IncrementalSweeping) {
FinishSweeping();
}
TEST_F(ConcurrentSweeperTest, SweepOnAllocationReturnEmptyPage) {
PreciseGC();
// First, allocate the full page of finalizable objects.
const size_t objects_to_allocated =
NormalPage::PayloadSize() /
(sizeof(HeapObjectHeader) + sizeof(NormalFinalizable));
auto* first_obj =
MakeGarbageCollected<NormalFinalizable>(GetAllocationHandle());
auto* finalizable_page =
NormalPage::FromInnerAddress(&HeapBase::From(GetHeapHandle()), first_obj);
for (size_t i = 1; i < objects_to_allocated; ++i) {
MakeGarbageCollected<NormalFinalizable>(GetAllocationHandle());
}
// Then, allocate a new unfinalizable object on a fresh page. We do that so
// that the sweeper on allocation doesn't allocate a new page.
auto* non_finalizable =
MakeGarbageCollected<NormalNonFinalizable>(GetAllocationHandle());
auto* non_finalizable_page = NormalPage::FromInnerAddress(
&HeapBase::From(GetHeapHandle()), non_finalizable);
ASSERT_NE(finalizable_page, non_finalizable_page);
// Start the GC without sweeping.
static constexpr GCConfig config = {
CollectionType::kMajor, StackState::kNoHeapPointers,
GCConfig::MarkingType::kAtomic,
GCConfig::SweepingType::kIncrementalAndConcurrent};
Heap::From(GetHeap())->CollectGarbage(config);
WaitForConcurrentSweeping();
// Allocate and sweep.
auto* allocated_after_sweeping =
MakeGarbageCollected<NormalFinalizable>(GetAllocationHandle());
// Check that the empty page of finalizable objects was returned.
EXPECT_EQ(finalizable_page,
NormalPage::FromInnerAddress(&HeapBase::From(GetHeapHandle()),
allocated_after_sweeping));
}
} // namespace internal
} // namespace cppgc