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

Side by Side Diff: cc/resources/worker_pool.cc

Issue 123113002: cc: Improve worker pool performance by using std:make_heap instead of std:priority_queue. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 11 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "cc/resources/worker_pool.h" 5 #include "cc/resources/worker_pool.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <queue>
9 8
10 #include "base/bind.h" 9 #include "base/bind.h"
11 #include "base/containers/hash_tables.h" 10 #include "base/containers/hash_tables.h"
12 #include "base/debug/trace_event.h" 11 #include "base/debug/trace_event.h"
13 #include "base/strings/stringprintf.h" 12 #include "base/strings/stringprintf.h"
14 #include "base/synchronization/condition_variable.h" 13 #include "base/synchronization/condition_variable.h"
15 #include "base/threading/simple_thread.h" 14 #include "base/threading/simple_thread.h"
16 #include "base/threading/thread_restrictions.h" 15 #include "base/threading/thread_restrictions.h"
17 #include "cc/base/scoped_ptr_deque.h" 16 #include "cc/base/scoped_ptr_deque.h"
18 17
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
91 // tasks are moved to |completed_tasks_| without being run. The result 90 // tasks are moved to |completed_tasks_| without being run. The result
92 // is that once scheduled, a task is guaranteed to end up in the 91 // is that once scheduled, a task is guaranteed to end up in the
93 // |completed_tasks_| queue even if they later get canceled by another 92 // |completed_tasks_| queue even if they later get canceled by another
94 // call to SetTaskGraph(). 93 // call to SetTaskGraph().
95 void SetTaskGraph(TaskGraph* graph); 94 void SetTaskGraph(TaskGraph* graph);
96 95
97 // Collect all completed tasks in |completed_tasks|. 96 // Collect all completed tasks in |completed_tasks|.
98 void CollectCompletedTasks(TaskVector* completed_tasks); 97 void CollectCompletedTasks(TaskVector* completed_tasks);
99 98
100 private: 99 private:
101 class PriorityComparator { 100 static bool CompareTaskPriority(const internal::GraphNode* a,
102 public: 101 const internal::GraphNode* b) {
103 bool operator()(const internal::GraphNode* a, 102 // In this system, numerically lower priority is run first.
104 const internal::GraphNode* b) { 103 if (a->priority() != b->priority())
105 // In this system, numerically lower priority is run first. 104 return a->priority() > b->priority();
106 if (a->priority() != b->priority())
107 return a->priority() > b->priority();
108 105
109 // Run task with most dependents first when priority is the same. 106 // Run task with most dependents first when priority is the same.
110 return a->dependents().size() < b->dependents().size(); 107 return a->dependents().size() < b->dependents().size();
111 } 108 }
112 };
113 109
114 // Overridden from base::DelegateSimpleThread: 110 // Overridden from base::DelegateSimpleThread:
115 virtual void Run() OVERRIDE; 111 virtual void Run() OVERRIDE;
116 112
117 // This lock protects all members of this class except 113 // This lock protects all members of this class except
118 // |worker_pool_on_origin_thread_|. Do not read or modify anything 114 // |worker_pool_on_origin_thread_|. Do not read or modify anything
119 // without holding this lock. Do not block while holding this lock. 115 // without holding this lock. Do not block while holding this lock.
120 mutable base::Lock lock_; 116 mutable base::Lock lock_;
121 117
122 // Condition variable that is waited on by worker threads until new 118 // Condition variable that is waited on by worker threads until new
123 // tasks are ready to run or shutdown starts. 119 // tasks are ready to run or shutdown starts.
124 base::ConditionVariable has_ready_to_run_tasks_cv_; 120 base::ConditionVariable has_ready_to_run_tasks_cv_;
125 121
126 // Provides each running thread loop with a unique index. First thread 122 // Provides each running thread loop with a unique index. First thread
127 // loop index is 0. 123 // loop index is 0.
128 unsigned next_thread_index_; 124 unsigned next_thread_index_;
129 125
130 // Set during shutdown. Tells workers to exit when no more tasks 126 // Set during shutdown. Tells workers to exit when no more tasks
131 // are pending. 127 // are pending.
132 bool shutdown_; 128 bool shutdown_;
133 129
134 // This set contains all pending tasks. 130 // This set contains all pending tasks.
135 GraphNodeMap pending_tasks_; 131 GraphNodeMap pending_tasks_;
136 132
137 // Ordered set of tasks that are ready to run. 133 // Priority queue containing tasks that are ready to run.
138 typedef std::priority_queue<internal::GraphNode*, 134 internal::GraphNode::Vector ready_to_run_tasks_;
139 std::vector<internal::GraphNode*>,
140 PriorityComparator> TaskQueue;
141 TaskQueue ready_to_run_tasks_;
142 135
143 // This set contains all currently running tasks. 136 // This set contains all currently running tasks.
144 GraphNodeMap running_tasks_; 137 GraphNodeMap running_tasks_;
145 138
146 // Completed tasks not yet collected by origin thread. 139 // Completed tasks not yet collected by origin thread.
147 TaskVector completed_tasks_; 140 TaskVector completed_tasks_;
148 141
149 ScopedPtrDeque<base::DelegateSimpleThread> workers_; 142 ScopedPtrDeque<base::DelegateSimpleThread> workers_;
150 143
151 DISALLOW_COPY_AND_ASSIGN(Inner); 144 DISALLOW_COPY_AND_ASSIGN(Inner);
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
206 worker->Join(); 199 worker->Join();
207 } 200 }
208 } 201 }
209 202
210 void WorkerPool::Inner::SetTaskGraph(TaskGraph* graph) { 203 void WorkerPool::Inner::SetTaskGraph(TaskGraph* graph) {
211 // It is OK to call SetTaskGraph() after shutdown if |graph| is empty. 204 // It is OK to call SetTaskGraph() after shutdown if |graph| is empty.
212 DCHECK(graph->empty() || !shutdown_); 205 DCHECK(graph->empty() || !shutdown_);
213 206
214 GraphNodeMap new_pending_tasks; 207 GraphNodeMap new_pending_tasks;
215 GraphNodeMap new_running_tasks; 208 GraphNodeMap new_running_tasks;
216 TaskQueue new_ready_to_run_tasks;
217 209
218 new_pending_tasks.swap(*graph); 210 new_pending_tasks.swap(*graph);
219 211
220 { 212 {
221 base::AutoLock lock(lock_); 213 base::AutoLock lock(lock_);
222 214
223 // First remove all completed tasks from |new_pending_tasks| and 215 // First remove all completed tasks from |new_pending_tasks| and
224 // adjust number of dependencies. 216 // adjust number of dependencies.
225 for (TaskVector::iterator it = completed_tasks_.begin(); 217 for (TaskVector::iterator it = completed_tasks_.begin();
226 it != completed_tasks_.end(); ++it) { 218 it != completed_tasks_.end(); ++it) {
(...skipping 16 matching lines...) Expand all
243 it != running_tasks_.end(); ++it) { 235 it != running_tasks_.end(); ++it) {
244 internal::WorkerPoolTask* task = it->first; 236 internal::WorkerPoolTask* task = it->first;
245 // Transfer scheduled task value from |new_pending_tasks| to 237 // Transfer scheduled task value from |new_pending_tasks| to
246 // |new_running_tasks| if currently running. Value must be set to 238 // |new_running_tasks| if currently running. Value must be set to
247 // NULL if |new_pending_tasks| doesn't contain task. This does 239 // NULL if |new_pending_tasks| doesn't contain task. This does
248 // the right in both cases. 240 // the right in both cases.
249 new_running_tasks.set(task, new_pending_tasks.take_and_erase(task)); 241 new_running_tasks.set(task, new_pending_tasks.take_and_erase(task));
250 } 242 }
251 243
252 // Build new "ready to run" tasks queue. 244 // Build new "ready to run" tasks queue.
253 // TODO(reveman): Create this queue when building the task graph instead. 245 ready_to_run_tasks_.clear();
254 for (GraphNodeMap::iterator it = new_pending_tasks.begin(); 246 for (GraphNodeMap::iterator it = new_pending_tasks.begin();
255 it != new_pending_tasks.end(); ++it) { 247 it != new_pending_tasks.end(); ++it) {
256 internal::WorkerPoolTask* task = it->first; 248 internal::WorkerPoolTask* task = it->first;
257 DCHECK(task); 249 DCHECK(task);
258 internal::GraphNode* node = it->second; 250 internal::GraphNode* node = it->second;
259 251
260 // Completed tasks should not exist in |new_pending_tasks|. 252 // Completed tasks should not exist in |new_pending_tasks|.
261 DCHECK(!task->HasFinishedRunning()); 253 DCHECK(!task->HasFinishedRunning());
262 254
263 // Call DidSchedule() to indicate that this task has been scheduled. 255 // Call DidSchedule() to indicate that this task has been scheduled.
264 // Note: This is only for debugging purposes. 256 // Note: This is only for debugging purposes.
265 task->DidSchedule(); 257 task->DidSchedule();
266 258
267 if (!node->num_dependencies()) 259 if (!node->num_dependencies())
268 new_ready_to_run_tasks.push(node); 260 ready_to_run_tasks_.push_back(node);
269 261
270 // Erase the task from old pending tasks. 262 // Erase the task from old pending tasks.
271 pending_tasks_.erase(task); 263 pending_tasks_.erase(task);
272 } 264 }
273 265
266 // Rearrange the elements in |ready_to_run_tasks_| in such a way that
267 // they form a heap.
268 std::make_heap(ready_to_run_tasks_.begin(),
269 ready_to_run_tasks_.end(),
270 CompareTaskPriority);
271
274 completed_tasks_.reserve(completed_tasks_.size() + pending_tasks_.size()); 272 completed_tasks_.reserve(completed_tasks_.size() + pending_tasks_.size());
275 273
276 // The items left in |pending_tasks_| need to be canceled. 274 // The items left in |pending_tasks_| need to be canceled.
277 for (GraphNodeMap::const_iterator it = pending_tasks_.begin(); 275 for (GraphNodeMap::const_iterator it = pending_tasks_.begin();
278 it != pending_tasks_.end(); 276 it != pending_tasks_.end();
279 ++it) { 277 ++it) {
280 completed_tasks_.push_back(it->first); 278 completed_tasks_.push_back(it->first);
281 } 279 }
282 280
283 // Swap task sets. 281 // Swap task sets.
284 // Note: old tasks are intentionally destroyed after releasing |lock_|. 282 // Note: old tasks are intentionally destroyed after releasing |lock_|.
285 pending_tasks_.swap(new_pending_tasks); 283 pending_tasks_.swap(new_pending_tasks);
286 running_tasks_.swap(new_running_tasks); 284 running_tasks_.swap(new_running_tasks);
287 std::swap(ready_to_run_tasks_, new_ready_to_run_tasks);
288 285
289 // If |ready_to_run_tasks_| is empty, it means we either have 286 // If |ready_to_run_tasks_| is empty, it means we either have
290 // running tasks, or we have no pending tasks. 287 // running tasks, or we have no pending tasks.
291 DCHECK(!ready_to_run_tasks_.empty() || 288 DCHECK(!ready_to_run_tasks_.empty() ||
292 (pending_tasks_.empty() || !running_tasks_.empty())); 289 (pending_tasks_.empty() || !running_tasks_.empty()));
293 290
294 // If there is more work available, wake up worker thread. 291 // If there is more work available, wake up worker thread.
295 if (!ready_to_run_tasks_.empty()) 292 if (!ready_to_run_tasks_.empty())
296 has_ready_to_run_tasks_cv_.Signal(); 293 has_ready_to_run_tasks_cv_.Signal();
297 } 294 }
(...skipping 17 matching lines...) Expand all
315 // Exit when shutdown is set and no more tasks are pending. 312 // Exit when shutdown is set and no more tasks are pending.
316 if (shutdown_ && pending_tasks_.empty()) 313 if (shutdown_ && pending_tasks_.empty())
317 break; 314 break;
318 315
319 // Wait for more tasks. 316 // Wait for more tasks.
320 has_ready_to_run_tasks_cv_.Wait(); 317 has_ready_to_run_tasks_cv_.Wait();
321 continue; 318 continue;
322 } 319 }
323 320
324 // Take top priority task from |ready_to_run_tasks_|. 321 // Take top priority task from |ready_to_run_tasks_|.
322 std::pop_heap(ready_to_run_tasks_.begin(),
323 ready_to_run_tasks_.end(),
324 CompareTaskPriority);
325 scoped_refptr<internal::WorkerPoolTask> task( 325 scoped_refptr<internal::WorkerPoolTask> task(
326 ready_to_run_tasks_.top()->task()); 326 ready_to_run_tasks_.back()->task());
327 ready_to_run_tasks_.pop(); 327 ready_to_run_tasks_.pop_back();
328 328
329 // Move task from |pending_tasks_| to |running_tasks_|. 329 // Move task from |pending_tasks_| to |running_tasks_|.
330 DCHECK(pending_tasks_.contains(task.get())); 330 DCHECK(pending_tasks_.contains(task.get()));
331 DCHECK(!running_tasks_.contains(task.get())); 331 DCHECK(!running_tasks_.contains(task.get()));
332 running_tasks_.set(task.get(), pending_tasks_.take_and_erase(task.get())); 332 running_tasks_.set(task.get(), pending_tasks_.take_and_erase(task.get()));
333 333
334 // There may be more work available, so wake up another worker thread. 334 // There may be more work available, so wake up another worker thread.
335 has_ready_to_run_tasks_cv_.Signal(); 335 has_ready_to_run_tasks_cv_.Signal();
336 336
337 // Call WillRun() before releasing |lock_| and running task. 337 // Call WillRun() before releasing |lock_| and running task.
(...skipping 14 matching lines...) Expand all
352 task.get()); 352 task.get());
353 if (node) { 353 if (node) {
354 for (internal::GraphNode::Vector::const_iterator it = 354 for (internal::GraphNode::Vector::const_iterator it =
355 node->dependents().begin(); 355 node->dependents().begin();
356 it != node->dependents().end(); ++it) { 356 it != node->dependents().end(); ++it) {
357 internal::GraphNode* dependent_node = *it; 357 internal::GraphNode* dependent_node = *it;
358 358
359 dependent_node->remove_dependency(); 359 dependent_node->remove_dependency();
360 // Task is ready if it has no dependencies. Add it to 360 // Task is ready if it has no dependencies. Add it to
361 // |ready_to_run_tasks_|. 361 // |ready_to_run_tasks_|.
362 if (!dependent_node->num_dependencies()) 362 if (!dependent_node->num_dependencies()) {
363 ready_to_run_tasks_.push(dependent_node); 363 ready_to_run_tasks_.push_back(dependent_node);
364 std::push_heap(ready_to_run_tasks_.begin(),
365 ready_to_run_tasks_.end(),
366 CompareTaskPriority);
367 }
364 } 368 }
365 } 369 }
366 370
367 // Finally add task to |completed_tasks_|. 371 // Finally add task to |completed_tasks_|.
368 completed_tasks_.push_back(task); 372 completed_tasks_.push_back(task);
369 } 373 }
370 374
371 // We noticed we should exit. Wake up the next worker so it knows it should 375 // We noticed we should exit. Wake up the next worker so it knows it should
372 // exit as well (because the Shutdown() code only signals once). 376 // exit as well (because the Shutdown() code only signals once).
373 has_ready_to_run_tasks_cv_.Signal(); 377 has_ready_to_run_tasks_cv_.Signal();
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
424 void WorkerPool::SetTaskGraph(TaskGraph* graph) { 428 void WorkerPool::SetTaskGraph(TaskGraph* graph) {
425 TRACE_EVENT1("cc", "WorkerPool::SetTaskGraph", 429 TRACE_EVENT1("cc", "WorkerPool::SetTaskGraph",
426 "num_tasks", graph->size()); 430 "num_tasks", graph->size());
427 431
428 DCHECK(!in_dispatch_completion_callbacks_); 432 DCHECK(!in_dispatch_completion_callbacks_);
429 433
430 inner_->SetTaskGraph(graph); 434 inner_->SetTaskGraph(graph);
431 } 435 }
432 436
433 } // namespace cc 437 } // namespace cc
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698