Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(501)

Side by Side Diff: third_party/WebKit/Source/platform/scheduler/base/intrusive_heap.h

Issue 2419793002: [Reland] Optimize blink scheduler with an intrusive heap (Closed)
Patch Set: Fixed perf test and increaced test coverage Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_INTRUSIVE_HEAP_H_
6 #define THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_INTRUSIVE_HEAP_H_
7
8 #include <algorithm>
9 #include <vector>
10
11 #include "base/logging.h"
12
13 namespace blink {
14 namespace scheduler {
15
16 template <typename T>
17 class IntrusiveHeap;
18
19 // Intended as an opaque wrapper around |index_|.
20 class HeapHandle {
21 public:
22 HeapHandle() : index_(0u) {}
23
24 bool IsValid() const { return index_ != 0u; }
25
26 private:
27 template <typename T>
28 friend class IntrusiveHeap;
29
30 HeapHandle(size_t index) : index_(index) {}
31
32 size_t index_;
33 };
34
35 // A standard min-heap with the following assumptions:
36 // 1. T has operator <=
37 // 2. T has method void SetHeapHandle(HeapHandle handle)
38 // 3. T has method void ClearHeapHandle()
39 // 4. T is moveable
40 // 5. T is default constructible
41 // 6. The heap size never gets terribly big so reclaiming memory on pop/erase
42 // isn't a priority.
43 //
44 // The reason IntrusiveHeap exists is to provide similar performance to
45 // std::priority_queue while allowing removal of arbitrary elements.
46 template <typename T>
47 class IntrusiveHeap {
48 public:
49 IntrusiveHeap() : nodes_(kMinimumHeapSize), size_(0) {}
50
51 ~IntrusiveHeap() {
52 for (size_t i = 1; i <= size_; i++) {
53 MakeHole(i);
54 }
55 }
56
57 bool empty() const { return size_ == 0; }
58
59 size_t size() const { return size_; }
60
61 void clear() {
62 for (size_t i = 1; i <= size_; i++) {
63 MakeHole(i);
64 }
65 nodes_.resize(kMinimumHeapSize);
66 size_ = 0;
67 }
68
69 const T& min() const {
70 DCHECK_GE(size_, 1u);
71 return nodes_[1];
72 }
73
74 void pop() {
75 DCHECK_GE(size_, 1u);
76 MakeHole(1u);
77 size_t top_index = size_--;
78 if (!empty())
79 MoveHoleDownAndFillWithLeafElement(1u, std::move(nodes_[top_index]));
80 }
81
82 void insert(T&& element) {
83 size_++;
84 if (size_ >= nodes_.size())
85 nodes_.resize(nodes_.size() * 2);
86 // Notionally we have a hole in the tree at index |size_|, move this up
87 // to find the right insertion point.
88 MoveHoleUpAndFillWithElement(size_, std::move(element));
89 }
90
91 void erase(HeapHandle handle) {
92 DCHECK_GT(handle.index_, 0u);
93 DCHECK_LE(handle.index_, size_);
94 MakeHole(handle.index_);
95 size_t top_index = size_--;
96 if (empty() || top_index == handle.index_)
97 return;
98 if (nodes_[handle.index_] <= nodes_[top_index]) {
99 MoveHoleDownAndFillWithLeafElement(handle.index_,
100 std::move(nodes_[top_index]));
101 } else {
102 MoveHoleUpAndFillWithElement(handle.index_, std::move(nodes_[top_index]));
103 }
104 }
105
106 void ReplaceMin(T&& element) {
107 // Note |element| might not be a leaf node so we can't use
108 // MoveHoleDownAndFillWithLeafElement.
109 MoveHoleDownAndFillWithElement(1u, std::move(element));
110 }
111
112 void ChangeKey(HeapHandle handle, T&& element) {
113 if (nodes_[handle.index_] <= element) {
114 MoveHoleDownAndFillWithLeafElement(handle.index_, std::move(element));
115 } else {
116 MoveHoleUpAndFillWithElement(handle.index_, std::move(element));
117 }
118 }
119
120 // Caution mutating the heap invalidates the iterators.
121 const T* begin() const { return &nodes_[1u]; }
122 const T* end() const { return begin() + size_; }
123
124 private:
125 enum {
126 // The majority of sets in the scheduler have 0-3 items in them (a few will
127 // have perhaps up to 100), so this means we usually only have to allocate
128 // memory once.
129 kMinimumHeapSize = 4u
130 };
131
132 size_t MoveHole(size_t new_hole_pos, size_t old_hole_pos) {
133 DCHECK_GT(new_hole_pos, 0u);
134 DCHECK_LE(new_hole_pos, size_);
135 DCHECK_GT(new_hole_pos, 0u);
136 DCHECK_LE(new_hole_pos, size_);
137 DCHECK_NE(old_hole_pos, new_hole_pos);
138 nodes_[old_hole_pos] = std::move(nodes_[new_hole_pos]);
139 nodes_[old_hole_pos].SetHeapHandle(HeapHandle(old_hole_pos));
140 return new_hole_pos;
141 }
142
143 // Notionally creates a hole in the tree at |index|.
144 void MakeHole(size_t index) {
145 DCHECK_GT(index, 0u);
146 DCHECK_LE(index, size_);
147 nodes_[index].ClearHeapHandle();
148 }
149
150 void FillHole(size_t hole, T&& element) {
151 DCHECK_GT(hole, 0u);
152 DCHECK_LE(hole, size_);
153 nodes_[hole] = std::move(element);
154 nodes_[hole].SetHeapHandle(HeapHandle(hole));
155 DCHECK(std::is_heap(begin(), end(), CompareNodes));
156 }
157
158 static bool CompareNodes(const T& a, const T& b) { return b <= a; }
159
160 // Moves the |hole| up the tree and when the right position has been found
161 // |element| is moved in.
162 void MoveHoleUpAndFillWithElement(size_t hole, T&& element) {
163 DCHECK_GT(hole, 0u);
164 DCHECK_LE(hole, size_);
165 while (hole >= 2u) {
166 size_t parent_pos = hole / 2;
167 if (nodes_[parent_pos] <= element)
168 break;
169
170 hole = MoveHole(parent_pos, hole);
171 }
172 FillHole(hole, std::move(element));
173 }
174
175 // Moves the |hole| down the tree and when the right position has been found
176 // |element| is moved in.
177 void MoveHoleDownAndFillWithElement(size_t hole, T&& element) {
178 DCHECK_GT(hole, 0u);
179 DCHECK_LE(hole, size_);
180 size_t child_pos = hole * 2;
181 while (child_pos < size_) {
182 if (nodes_[child_pos + 1] <= nodes_[child_pos])
183 child_pos++;
184
185 if (element <= nodes_[child_pos])
186 break;
187
188 hole = MoveHole(child_pos, hole);
189 child_pos *= 2;
190 }
191 if (child_pos == size_ && !(element <= nodes_[child_pos]))
192 hole = MoveHole(child_pos, hole);
193 FillHole(hole, std::move(element));
194 }
195
196 // Moves the |hole| down the tree and when the right position has been found
197 // |leaf_element| is moved in. Faster than MoveHoleDownAndFillWithElement
198 // (it does one key comparison per level instead of two) but only valid for
199 // leaf elements (i.e. one of the max values).
200 void MoveHoleDownAndFillWithLeafElement(size_t hole, T&& leaf_element) {
201 DCHECK_GT(hole, 0u);
202 DCHECK_LE(hole, size_);
203 size_t child_pos = hole * 2;
204 while (child_pos < size_) {
205 size_t second_child = child_pos + 1;
206 if (nodes_[second_child] <= nodes_[child_pos])
207 child_pos = second_child;
208
209 hole = MoveHole(child_pos, hole);
210 child_pos *= 2;
211 }
212 if (child_pos == size_)
213 hole = MoveHole(child_pos, hole);
214 MoveHoleUpAndFillWithElement(hole, std::move(leaf_element));
215 }
216
217 std::vector<T> nodes_; // NOTE we use 1-based indexing
218 size_t size_;
219 };
220
221 } // namespace scheduler
222 } // namespace blink
223
224 #endif // THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_INTRUSIVE_HEAP_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698