1// Copyright 2018 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#include "util/worker_pool.h" 6 7#include "base/command_line.h" 8#include "base/strings/string_number_conversions.h" 9#include "gn/switches.h" 10#include "util/sys_info.h" 11 12namespace { 13 14int GetThreadCount() { 15 std::string thread_count = 16 base::CommandLine::ForCurrentProcess()->GetSwitchValueString( 17 switches::kThreads); 18 19 // See if an override was specified on the command line. 20 int result; 21 if (!thread_count.empty() && base::StringToInt(thread_count, &result) && 22 result >= 1) { 23 return result; 24 } 25 26 // Base the default number of worker threads on number of cores in the 27 // system. When building large projects, the speed can be limited by how fast 28 // the main thread can dispatch work and connect the dependency graph. If 29 // there are too many worker threads, the main thread can be starved and it 30 // will run slower overall. 31 // 32 // One less worker thread than the number of physical CPUs seems to be a 33 // good value, both theoretically and experimentally. But always use at 34 // least some workers to prevent us from being too sensitive to I/O latency 35 // on low-end systems. 36 // 37 // The minimum thread count is based on measuring the optimal threads for the 38 // Chrome build on a several-year-old 4-core MacBook. 39 // Almost all CPUs now are hyperthreaded. 40 int num_cores = NumberOfProcessors() / 2; 41 return std::max(num_cores - 1, 8); 42} 43 44} // namespace 45 46WorkerPool::WorkerPool() : WorkerPool(GetThreadCount()) {} 47 48WorkerPool::WorkerPool(size_t thread_count) : should_stop_processing_(false) { 49 threads_.reserve(thread_count); 50 for (size_t i = 0; i < thread_count; ++i) 51 threads_.emplace_back([this]() { Worker(); }); 52} 53 54WorkerPool::~WorkerPool() { 55 { 56 std::unique_lock<std::mutex> queue_lock(queue_mutex_); 57 should_stop_processing_ = true; 58 } 59 60 pool_notifier_.notify_all(); 61 62 for (auto& task_thread : threads_) { 63 task_thread.join(); 64 } 65} 66 67void WorkerPool::PostTask(std::function<void()> work) { 68 { 69 std::unique_lock<std::mutex> queue_lock(queue_mutex_); 70 CHECK(!should_stop_processing_); 71 task_queue_.emplace(std::move(work)); 72 } 73 74 pool_notifier_.notify_one(); 75} 76 77void WorkerPool::Worker() { 78 for (;;) { 79 std::function<void()> task; 80 81 { 82 std::unique_lock<std::mutex> queue_lock(queue_mutex_); 83 84 pool_notifier_.wait(queue_lock, [this]() { 85 return (!task_queue_.empty()) || should_stop_processing_; 86 }); 87 88 if (should_stop_processing_ && task_queue_.empty()) 89 return; 90 91 task = std::move(task_queue_.front()); 92 task_queue_.pop(); 93 } 94 95 task(); 96 } 97} 98