Commit 5d9822b1 authored by Etienne Pierre-doray's avatar Etienne Pierre-doray Committed by Commit Bot

[Blink heap]: Track GlobalSize in worklist.

GlobalSize will be used as a hint to schedule marking work in
https://chromium-review.googlesource.com/c/chromium/src/+/2029609

This is implemented as an atomic variable that's updated when adding/removing
segments.

Bug: 1046343
Change-Id: Ia3f3be0539ad925b95a57d3480d064e37ccb6141
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2030644
Commit-Queue: Etienne Pierre-Doray <etiennep@chromium.org>
Reviewed-by: default avatarMichael Lippautz <mlippautz@chromium.org>
Reviewed-by: default avatarOmer Katz <omerkatz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#737276}
parent 66baa955
......@@ -11,6 +11,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_WORKLIST_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_WORKLIST_H_
#include <atomic>
#include <cstddef>
#include <utility>
......@@ -153,6 +154,9 @@ class Worklist {
private_push_segment(task_id)->Size();
}
// Thread-safe but may return an outdated result.
size_t GlobalPoolSize() const { return global_pool_.Size(); }
size_t LocalPushSegmentSize(int task_id) const {
return private_push_segment(task_id)->Size();
}
......@@ -196,8 +200,7 @@ class Worklist {
}
void MergeGlobalPool(Worklist* other) {
auto pair = other->global_pool_.Extract();
global_pool_.MergeList(pair.first, pair.second);
global_pool_.Merge(&other->global_pool_);
}
int num_tasks() const { return num_tasks_; }
......@@ -284,11 +287,14 @@ class Worklist {
base::AutoLock guard(lock_);
segment->set_next(top_);
set_top(segment);
size_.fetch_add(1, std::memory_order_relaxed);
}
inline bool Pop(Segment** segment) {
base::AutoLock guard(lock_);
if (top_) {
DCHECK_LT(0U, size_);
size_.fetch_sub(1, std::memory_order_relaxed);
*segment = top_;
set_top(top_->next());
return true;
......@@ -301,8 +307,16 @@ class Worklist {
reinterpret_cast<const base::subtle::AtomicWord*>(&top_)) == 0;
}
inline size_t Size() const {
// It is safe to read |size_| without a lock since this variable is
// atomic, keeping in mind that threads may not immediately see the new
// value when it is updated.
return TS_UNCHECKED_READ(size_).load(std::memory_order_relaxed);
}
void Clear() {
base::AutoLock guard(lock_);
size_.store(0, std::memory_order_relaxed);
Segment* current = top_;
while (current) {
Segment* tmp = current;
......@@ -321,6 +335,8 @@ class Worklist {
while (current) {
current->Update(callback);
if (current->IsEmpty()) {
DCHECK_LT(0U, size_);
size_.fetch_sub(1, std::memory_order_relaxed);
if (!prev) {
top_ = current->next();
} else {
......@@ -345,28 +361,28 @@ class Worklist {
}
}
std::pair<Segment*, Segment*> Extract() {
void Merge(GlobalPool* other) {
Segment* top = nullptr;
size_t other_size = 0;
{
base::AutoLock guard(lock_);
if (!top_)
return std::make_pair(nullptr, nullptr);
top = top_;
set_top(nullptr);
base::AutoLock guard(other->lock_);
if (!other->top_)
return;
top = other->top_;
other_size = other->size_.load(std::memory_order_relaxed);
other->size_.store(0, std::memory_order_relaxed);
other->set_top(nullptr);
}
Segment* end = top;
while (end->next())
end = end->next();
return std::make_pair(top, end);
}
void MergeList(Segment* start, Segment* end) {
if (!start)
return;
{
base::AutoLock guard(lock_);
size_.fetch_add(other_size, std::memory_order_relaxed);
end->set_next(top_);
set_top(start);
set_top(top);
}
}
......@@ -378,7 +394,8 @@ class Worklist {
}
mutable base::Lock lock_;
Segment* top_;
Segment* top_ GUARDED_BY(lock_);
std::atomic<size_t> size_ GUARDED_BY(lock_){0};
};
ALWAYS_INLINE Segment*& private_push_segment(int task_id) {
......
......@@ -155,13 +155,16 @@ TEST(WorklistTest, LocalPushStaysPrivate) {
SomeObject dummy;
SomeObject* retrieved = nullptr;
EXPECT_TRUE(worklist.IsGlobalEmpty());
EXPECT_EQ(0U, worklist.GlobalPoolSize());
EXPECT_TRUE(worklist_view1.Push(&dummy));
EXPECT_FALSE(worklist.IsGlobalEmpty());
EXPECT_EQ(0U, worklist.GlobalPoolSize());
EXPECT_FALSE(worklist_view2.Pop(&retrieved));
EXPECT_EQ(nullptr, retrieved);
EXPECT_TRUE(worklist_view1.Pop(&retrieved));
EXPECT_EQ(&dummy, retrieved);
EXPECT_TRUE(worklist.IsGlobalEmpty());
EXPECT_EQ(0U, worklist.GlobalPoolSize());
}
TEST(WorklistTest, GlobalUpdateNull) {
......@@ -175,6 +178,7 @@ TEST(WorklistTest, GlobalUpdateNull) {
EXPECT_TRUE(worklist_view.Push(object));
worklist.Update([](SomeObject* object, SomeObject** out) { return false; });
EXPECT_TRUE(worklist.IsGlobalEmpty());
EXPECT_EQ(0U, worklist.GlobalPoolSize());
}
TEST(WorklistTest, GlobalUpdate) {
......@@ -216,6 +220,7 @@ TEST(WorklistTest, FlushToGlobalPushSegment) {
objectA = reinterpret_cast<SomeObject*>(&objectA);
EXPECT_TRUE(worklist_view0.Push(objectA));
worklist.FlushToGlobal(0);
EXPECT_EQ(1U, worklist.GlobalPoolSize());
EXPECT_TRUE(worklist_view1.Pop(&object));
}
......@@ -230,6 +235,7 @@ TEST(WorklistTest, FlushToGlobalPopSegment) {
EXPECT_TRUE(worklist_view0.Push(objectA));
EXPECT_TRUE(worklist_view0.Pop(&object));
worklist.FlushToGlobal(0);
EXPECT_EQ(1U, worklist.GlobalPoolSize());
EXPECT_TRUE(worklist_view1.Pop(&object));
}
......@@ -242,8 +248,10 @@ TEST(WorklistTest, Clear) {
EXPECT_TRUE(worklist_view.Push(object));
}
EXPECT_TRUE(worklist_view.Push(object));
EXPECT_EQ(1U, worklist.GlobalPoolSize());
worklist.Clear();
EXPECT_TRUE(worklist.IsGlobalEmpty());
EXPECT_EQ(0U, worklist.GlobalPoolSize());
}
TEST(WorklistTest, SingleSegmentSteal) {
......@@ -259,6 +267,7 @@ TEST(WorklistTest, SingleSegmentSteal) {
EXPECT_TRUE(worklist_view1.Push(nullptr));
EXPECT_TRUE(worklist_view1.Pop(&retrieved));
EXPECT_EQ(nullptr, retrieved);
EXPECT_EQ(1U, worklist.GlobalPoolSize());
// Stealing.
for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
EXPECT_TRUE(worklist_view2.Pop(&retrieved));
......@@ -266,6 +275,7 @@ TEST(WorklistTest, SingleSegmentSteal) {
EXPECT_FALSE(worklist_view1.Pop(&retrieved));
}
EXPECT_TRUE(worklist.IsGlobalEmpty());
EXPECT_EQ(0U, worklist.GlobalPoolSize());
}
TEST(WorklistTest, MultipleSegmentsStolen) {
......@@ -287,11 +297,13 @@ TEST(WorklistTest, MultipleSegmentsStolen) {
EXPECT_TRUE(worklist_view1.Push(&dummy3));
EXPECT_TRUE(worklist_view1.Pop(&retrieved));
EXPECT_EQ(&dummy3, retrieved);
EXPECT_EQ(2U, worklist.GlobalPoolSize());
// Stealing.
EXPECT_TRUE(worklist_view2.Pop(&retrieved));
SomeObject* const expect_bag2 = retrieved;
EXPECT_TRUE(worklist_view3.Pop(&retrieved));
SomeObject* const expect_bag3 = retrieved;
EXPECT_EQ(0U, worklist.GlobalPoolSize());
EXPECT_NE(expect_bag2, expect_bag3);
EXPECT_TRUE(expect_bag2 == &dummy1 || expect_bag2 == &dummy2);
EXPECT_TRUE(expect_bag3 == &dummy1 || expect_bag3 == &dummy2);
......@@ -320,10 +332,13 @@ TEST(WorklistTest, MergeGlobalPool) {
EXPECT_TRUE(worklist_view1.Push(nullptr));
EXPECT_TRUE(worklist_view1.Pop(&retrieved));
EXPECT_EQ(nullptr, retrieved);
EXPECT_EQ(1U, worklist1.GlobalPoolSize());
// Merging global pool into a new Worklist.
TestWorklist worklist2;
TestWorklist::View worklist_view2(&worklist2, 0);
EXPECT_EQ(0U, worklist2.GlobalPoolSize());
worklist2.MergeGlobalPool(&worklist1);
EXPECT_EQ(1U, worklist2.GlobalPoolSize());
EXPECT_FALSE(worklist2.IsGlobalEmpty());
for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
EXPECT_TRUE(worklist_view2.Pop(&retrieved));
......
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