1*0b57cec5SDimitry Andric //===- llvm/Support/Parallel.cpp - Parallel algorithms --------------------===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric 9*0b57cec5SDimitry Andric #include "llvm/Support/Parallel.h" 10*0b57cec5SDimitry Andric #include "llvm/Config/llvm-config.h" 11480093f4SDimitry Andric #include "llvm/Support/ManagedStatic.h" 12*0b57cec5SDimitry Andric #include "llvm/Support/Threading.h" 13*0b57cec5SDimitry Andric 14*0b57cec5SDimitry Andric #include <atomic> 15480093f4SDimitry Andric #include <future> 16*0b57cec5SDimitry Andric #include <stack> 17*0b57cec5SDimitry Andric #include <thread> 18480093f4SDimitry Andric #include <vector> 19*0b57cec5SDimitry Andric 205ffd83dbSDimitry Andric llvm::ThreadPoolStrategy llvm::parallel::strategy; 215ffd83dbSDimitry Andric 225ffd83dbSDimitry Andric #if LLVM_ENABLE_THREADS 235ffd83dbSDimitry Andric 24*0b57cec5SDimitry Andric namespace llvm { 25*0b57cec5SDimitry Andric namespace parallel { 26*0b57cec5SDimitry Andric namespace detail { 27*0b57cec5SDimitry Andric 28*0b57cec5SDimitry Andric namespace { 29*0b57cec5SDimitry Andric 30*0b57cec5SDimitry Andric /// An abstract class that takes closures and runs them asynchronously. 31*0b57cec5SDimitry Andric class Executor { 32*0b57cec5SDimitry Andric public: 33*0b57cec5SDimitry Andric virtual ~Executor() = default; 34*0b57cec5SDimitry Andric virtual void add(std::function<void()> func) = 0; 35*0b57cec5SDimitry Andric 36*0b57cec5SDimitry Andric static Executor *getDefaultExecutor(); 37*0b57cec5SDimitry Andric }; 38*0b57cec5SDimitry Andric 39*0b57cec5SDimitry Andric /// An implementation of an Executor that runs closures on a thread pool 40*0b57cec5SDimitry Andric /// in filo order. 41*0b57cec5SDimitry Andric class ThreadPoolExecutor : public Executor { 42*0b57cec5SDimitry Andric public: 435ffd83dbSDimitry Andric explicit ThreadPoolExecutor(ThreadPoolStrategy S = hardware_concurrency()) { 445ffd83dbSDimitry Andric unsigned ThreadCount = S.compute_thread_count(); 45*0b57cec5SDimitry Andric // Spawn all but one of the threads in another thread as spawning threads 46*0b57cec5SDimitry Andric // can take a while. 47480093f4SDimitry Andric Threads.reserve(ThreadCount); 48480093f4SDimitry Andric Threads.resize(1); 49480093f4SDimitry Andric std::lock_guard<std::mutex> Lock(Mutex); 505ffd83dbSDimitry Andric Threads[0] = std::thread([this, ThreadCount, S] { 515ffd83dbSDimitry Andric for (unsigned I = 1; I < ThreadCount; ++I) { 525ffd83dbSDimitry Andric Threads.emplace_back([=] { work(S, I); }); 53480093f4SDimitry Andric if (Stop) 54480093f4SDimitry Andric break; 55*0b57cec5SDimitry Andric } 56480093f4SDimitry Andric ThreadsCreated.set_value(); 575ffd83dbSDimitry Andric work(S, 0); 58480093f4SDimitry Andric }); 59480093f4SDimitry Andric } 60480093f4SDimitry Andric 61480093f4SDimitry Andric void stop() { 62480093f4SDimitry Andric { 63480093f4SDimitry Andric std::lock_guard<std::mutex> Lock(Mutex); 64480093f4SDimitry Andric if (Stop) 65480093f4SDimitry Andric return; 66480093f4SDimitry Andric Stop = true; 67480093f4SDimitry Andric } 68480093f4SDimitry Andric Cond.notify_all(); 69480093f4SDimitry Andric ThreadsCreated.get_future().wait(); 70*0b57cec5SDimitry Andric } 71*0b57cec5SDimitry Andric 72*0b57cec5SDimitry Andric ~ThreadPoolExecutor() override { 73480093f4SDimitry Andric stop(); 74480093f4SDimitry Andric std::thread::id CurrentThreadId = std::this_thread::get_id(); 75480093f4SDimitry Andric for (std::thread &T : Threads) 76480093f4SDimitry Andric if (T.get_id() == CurrentThreadId) 77480093f4SDimitry Andric T.detach(); 78480093f4SDimitry Andric else 79480093f4SDimitry Andric T.join(); 80*0b57cec5SDimitry Andric } 81*0b57cec5SDimitry Andric 825ffd83dbSDimitry Andric struct Creator { 835ffd83dbSDimitry Andric static void *call() { return new ThreadPoolExecutor(strategy); } 845ffd83dbSDimitry Andric }; 85480093f4SDimitry Andric struct Deleter { 86480093f4SDimitry Andric static void call(void *Ptr) { ((ThreadPoolExecutor *)Ptr)->stop(); } 87480093f4SDimitry Andric }; 88480093f4SDimitry Andric 89*0b57cec5SDimitry Andric void add(std::function<void()> F) override { 90480093f4SDimitry Andric { 91480093f4SDimitry Andric std::lock_guard<std::mutex> Lock(Mutex); 9281ad6265SDimitry Andric WorkStack.push(std::move(F)); 93480093f4SDimitry Andric } 94*0b57cec5SDimitry Andric Cond.notify_one(); 95*0b57cec5SDimitry Andric } 96*0b57cec5SDimitry Andric 97*0b57cec5SDimitry Andric private: 985ffd83dbSDimitry Andric void work(ThreadPoolStrategy S, unsigned ThreadID) { 995ffd83dbSDimitry Andric S.apply_thread_strategy(ThreadID); 100*0b57cec5SDimitry Andric while (true) { 101*0b57cec5SDimitry Andric std::unique_lock<std::mutex> Lock(Mutex); 102*0b57cec5SDimitry Andric Cond.wait(Lock, [&] { return Stop || !WorkStack.empty(); }); 103*0b57cec5SDimitry Andric if (Stop) 104*0b57cec5SDimitry Andric break; 10581ad6265SDimitry Andric auto Task = std::move(WorkStack.top()); 106*0b57cec5SDimitry Andric WorkStack.pop(); 107*0b57cec5SDimitry Andric Lock.unlock(); 108*0b57cec5SDimitry Andric Task(); 109*0b57cec5SDimitry Andric } 110*0b57cec5SDimitry Andric } 111*0b57cec5SDimitry Andric 112*0b57cec5SDimitry Andric std::atomic<bool> Stop{false}; 113*0b57cec5SDimitry Andric std::stack<std::function<void()>> WorkStack; 114*0b57cec5SDimitry Andric std::mutex Mutex; 115*0b57cec5SDimitry Andric std::condition_variable Cond; 116480093f4SDimitry Andric std::promise<void> ThreadsCreated; 117480093f4SDimitry Andric std::vector<std::thread> Threads; 118*0b57cec5SDimitry Andric }; 119*0b57cec5SDimitry Andric 120*0b57cec5SDimitry Andric Executor *Executor::getDefaultExecutor() { 121480093f4SDimitry Andric // The ManagedStatic enables the ThreadPoolExecutor to be stopped via 122480093f4SDimitry Andric // llvm_shutdown() which allows a "clean" fast exit, e.g. via _exit(). This 123480093f4SDimitry Andric // stops the thread pool and waits for any worker thread creation to complete 124480093f4SDimitry Andric // but does not wait for the threads to finish. The wait for worker thread 125480093f4SDimitry Andric // creation to complete is important as it prevents intermittent crashes on 126480093f4SDimitry Andric // Windows due to a race condition between thread creation and process exit. 127480093f4SDimitry Andric // 128480093f4SDimitry Andric // The ThreadPoolExecutor will only be destroyed when the static unique_ptr to 129480093f4SDimitry Andric // it is destroyed, i.e. in a normal full exit. The ThreadPoolExecutor 130480093f4SDimitry Andric // destructor ensures it has been stopped and waits for worker threads to 131480093f4SDimitry Andric // finish. The wait is important as it prevents intermittent crashes on 132480093f4SDimitry Andric // Windows when the process is doing a full exit. 133480093f4SDimitry Andric // 134480093f4SDimitry Andric // The Windows crashes appear to only occur with the MSVC static runtimes and 135480093f4SDimitry Andric // are more frequent with the debug static runtime. 136480093f4SDimitry Andric // 137480093f4SDimitry Andric // This also prevents intermittent deadlocks on exit with the MinGW runtime. 1385ffd83dbSDimitry Andric 1395ffd83dbSDimitry Andric static ManagedStatic<ThreadPoolExecutor, ThreadPoolExecutor::Creator, 140480093f4SDimitry Andric ThreadPoolExecutor::Deleter> 141480093f4SDimitry Andric ManagedExec; 142480093f4SDimitry Andric static std::unique_ptr<ThreadPoolExecutor> Exec(&(*ManagedExec)); 143480093f4SDimitry Andric return Exec.get(); 144*0b57cec5SDimitry Andric } 1458bcb0991SDimitry Andric } // namespace 146*0b57cec5SDimitry Andric 147*0b57cec5SDimitry Andric static std::atomic<int> TaskGroupInstances; 148*0b57cec5SDimitry Andric 149*0b57cec5SDimitry Andric // Latch::sync() called by the dtor may cause one thread to block. If is a dead 150*0b57cec5SDimitry Andric // lock if all threads in the default executor are blocked. To prevent the dead 151*0b57cec5SDimitry Andric // lock, only allow the first TaskGroup to run tasks parallelly. In the scenario 152*0b57cec5SDimitry Andric // of nested parallel_for_each(), only the outermost one runs parallelly. 153*0b57cec5SDimitry Andric TaskGroup::TaskGroup() : Parallel(TaskGroupInstances++ == 0) {} 154349cc55cSDimitry Andric TaskGroup::~TaskGroup() { 155349cc55cSDimitry Andric // We must ensure that all the workloads have finished before decrementing the 156349cc55cSDimitry Andric // instances count. 157349cc55cSDimitry Andric L.sync(); 158349cc55cSDimitry Andric --TaskGroupInstances; 159349cc55cSDimitry Andric } 160*0b57cec5SDimitry Andric 161*0b57cec5SDimitry Andric void TaskGroup::spawn(std::function<void()> F) { 162*0b57cec5SDimitry Andric if (Parallel) { 163*0b57cec5SDimitry Andric L.inc(); 16481ad6265SDimitry Andric Executor::getDefaultExecutor()->add([&, F = std::move(F)] { 165*0b57cec5SDimitry Andric F(); 166*0b57cec5SDimitry Andric L.dec(); 167*0b57cec5SDimitry Andric }); 168*0b57cec5SDimitry Andric } else { 169*0b57cec5SDimitry Andric F(); 170*0b57cec5SDimitry Andric } 171*0b57cec5SDimitry Andric } 172*0b57cec5SDimitry Andric 173*0b57cec5SDimitry Andric } // namespace detail 174*0b57cec5SDimitry Andric } // namespace parallel 175*0b57cec5SDimitry Andric } // namespace llvm 176*0b57cec5SDimitry Andric #endif // LLVM_ENABLE_THREADS 17704eeddc0SDimitry Andric 17881ad6265SDimitry Andric void llvm::parallelFor(size_t Begin, size_t End, 17904eeddc0SDimitry Andric llvm::function_ref<void(size_t)> Fn) { 18004eeddc0SDimitry Andric // If we have zero or one items, then do not incur the overhead of spinning up 18104eeddc0SDimitry Andric // a task group. They are surprisingly expensive, and because they do not 18204eeddc0SDimitry Andric // support nested parallelism, a single entry task group can block parallel 18304eeddc0SDimitry Andric // execution underneath them. 18404eeddc0SDimitry Andric #if LLVM_ENABLE_THREADS 18504eeddc0SDimitry Andric auto NumItems = End - Begin; 18604eeddc0SDimitry Andric if (NumItems > 1 && parallel::strategy.ThreadsRequested != 1) { 18704eeddc0SDimitry Andric // Limit the number of tasks to MaxTasksPerGroup to limit job scheduling 18804eeddc0SDimitry Andric // overhead on large inputs. 18904eeddc0SDimitry Andric auto TaskSize = NumItems / parallel::detail::MaxTasksPerGroup; 19004eeddc0SDimitry Andric if (TaskSize == 0) 19104eeddc0SDimitry Andric TaskSize = 1; 19204eeddc0SDimitry Andric 19304eeddc0SDimitry Andric parallel::detail::TaskGroup TG; 19404eeddc0SDimitry Andric for (; Begin + TaskSize < End; Begin += TaskSize) { 19504eeddc0SDimitry Andric TG.spawn([=, &Fn] { 19604eeddc0SDimitry Andric for (size_t I = Begin, E = Begin + TaskSize; I != E; ++I) 19704eeddc0SDimitry Andric Fn(I); 19804eeddc0SDimitry Andric }); 19904eeddc0SDimitry Andric } 20004eeddc0SDimitry Andric for (; Begin != End; ++Begin) 20104eeddc0SDimitry Andric Fn(Begin); 20204eeddc0SDimitry Andric return; 20304eeddc0SDimitry Andric } 20404eeddc0SDimitry Andric #endif 20504eeddc0SDimitry Andric 20604eeddc0SDimitry Andric for (; Begin != End; ++Begin) 20704eeddc0SDimitry Andric Fn(Begin); 20804eeddc0SDimitry Andric } 209