diff --git a/BUILD.gn b/BUILD.gn index dfd64d3bd5..7f744a9b91 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -2595,6 +2595,8 @@ v8_source_set("v8_base_without_compiler") { "src/heap/incremental-marking-job.h", "src/heap/incremental-marking.cc", "src/heap/incremental-marking.h", + "src/heap/index-generator.cc", + "src/heap/index-generator.h", "src/heap/invalidated-slots-inl.h", "src/heap/invalidated-slots.cc", "src/heap/invalidated-slots.h", diff --git a/src/heap/index-generator.cc b/src/heap/index-generator.cc new file mode 100644 index 0000000000..150e3fa159 --- /dev/null +++ b/src/heap/index-generator.cc @@ -0,0 +1,48 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/heap/index-generator.h" + +namespace v8 { +namespace internal { + +IndexGenerator::IndexGenerator(size_t size) : size_(size) { + if (size == 0) return; + base::MutexGuard guard(&lock_); + pending_indices_.push(0); + ranges_to_split_.push({0, size_}); +} + +base::Optional IndexGenerator::GetNext() { + base::MutexGuard guard(&lock_); + if (!pending_indices_.empty()) { + // Return any pending index first. + auto index = pending_indices_.top(); + pending_indices_.pop(); + return index; + } + if (ranges_to_split_.empty()) return base::nullopt; + + // Split the oldest running range in 2 and return the middle index as + // starting point. + auto range = ranges_to_split_.front(); + ranges_to_split_.pop(); + size_t size = range.second - range.first; + size_t mid = range.first + size / 2; + // Both sides of the range are added to |ranges_to_split_| so they may be + // further split if possible. + if (mid - range.first > 1) ranges_to_split_.push({range.first, mid}); + if (range.second - mid > 1) ranges_to_split_.push({mid, range.second}); + return mid; +} + +void IndexGenerator::GiveBack(size_t index) { + base::MutexGuard guard(&lock_); + // Add |index| to pending indices so GetNext() may return it before anything + // else. + pending_indices_.push(index); +} + +} // namespace internal +} // namespace v8 diff --git a/src/heap/index-generator.h b/src/heap/index-generator.h new file mode 100644 index 0000000000..0e4df1714e --- /dev/null +++ b/src/heap/index-generator.h @@ -0,0 +1,43 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_HEAP_INDEX_GENERATOR_H_ +#define V8_HEAP_INDEX_GENERATOR_H_ + +#include +#include +#include + +#include "src/base/macros.h" +#include "src/base/optional.h" +#include "src/base/platform/mutex.h" + +namespace v8 { +namespace internal { + +// A thread-safe data structure that generates heuristic starting points in a +// range to process items in parallel. +class V8_EXPORT_PRIVATE IndexGenerator { + public: + explicit IndexGenerator(size_t size); + IndexGenerator(const IndexGenerator&) = delete; + IndexGenerator& operator=(const IndexGenerator&) = delete; + + base::Optional GetNext(); + void GiveBack(size_t index); + + private: + base::Mutex lock_; + // Pending indices that are ready to be handed out, prioritized over + // |pending_ranges_| when non-empty. + std::stack pending_indices_; + // Pending [start, end] (exclusive) ranges to split and hand out indices from. + std::queue> ranges_to_split_; + const size_t size_; +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_HEAP_INDEX_GENERATOR_H_ diff --git a/test/unittests/BUILD.gn b/test/unittests/BUILD.gn index 5b2cc7b289..4e792ad469 100644 --- a/test/unittests/BUILD.gn +++ b/test/unittests/BUILD.gn @@ -279,6 +279,7 @@ v8_source_set("unittests_sources") { "heap/heap-controller-unittest.cc", "heap/heap-unittest.cc", "heap/heap-utils.h", + "heap/index-generator-unittest.cc", "heap/item-parallel-job-unittest.cc", "heap/js-member-unittest.cc", "heap/list-unittest.cc", diff --git a/test/unittests/heap/index-generator-unittest.cc b/test/unittests/heap/index-generator-unittest.cc new file mode 100644 index 0000000000..11627741b8 --- /dev/null +++ b/test/unittests/heap/index-generator-unittest.cc @@ -0,0 +1,50 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/heap/index-generator.h" + +#include "test/unittests/test-utils.h" + +namespace v8 { +namespace internal { + +TEST(IndexGeneratorTest, Empty) { + IndexGenerator gen(0); + + EXPECT_EQ(base::nullopt, gen.GetNext()); +} + +TEST(IndexGeneratorTest, GetNext) { + IndexGenerator gen(11); + + EXPECT_EQ(0U, gen.GetNext()); + EXPECT_EQ(5U, gen.GetNext()); + EXPECT_EQ(2U, gen.GetNext()); + EXPECT_EQ(8U, gen.GetNext()); + EXPECT_EQ(1U, gen.GetNext()); + EXPECT_EQ(3U, gen.GetNext()); + EXPECT_EQ(6U, gen.GetNext()); + EXPECT_EQ(9U, gen.GetNext()); + EXPECT_EQ(4U, gen.GetNext()); + EXPECT_EQ(7U, gen.GetNext()); + EXPECT_EQ(10U, gen.GetNext()); + EXPECT_EQ(base::nullopt, gen.GetNext()); +} + +TEST(IndexGeneratorTest, GiveBack) { + IndexGenerator gen(4); + + EXPECT_EQ(0U, gen.GetNext()); + EXPECT_EQ(2U, gen.GetNext()); + EXPECT_EQ(1U, gen.GetNext()); + gen.GiveBack(2); + gen.GiveBack(0); + EXPECT_EQ(0U, gen.GetNext()); + EXPECT_EQ(2U, gen.GetNext()); + EXPECT_EQ(3U, gen.GetNext()); + EXPECT_EQ(base::nullopt, gen.GetNext()); +} + +} // namespace internal +} // namespace v8