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 22*0b57cec5SDimitry Andric namespace llvm { 23*0b57cec5SDimitry Andric namespace parallel { 24bdd1243dSDimitry Andric #if LLVM_ENABLE_THREADS 25bdd1243dSDimitry Andric 26bdd1243dSDimitry Andric #ifdef _WIN32 27bdd1243dSDimitry Andric static thread_local unsigned threadIndex; 28bdd1243dSDimitry Andric 29bdd1243dSDimitry Andric unsigned getThreadIndex() { return threadIndex; } 30bdd1243dSDimitry Andric #else 31bdd1243dSDimitry Andric thread_local unsigned threadIndex; 32bdd1243dSDimitry Andric #endif 33bdd1243dSDimitry Andric 34*0b57cec5SDimitry Andric namespace detail { 35*0b57cec5SDimitry Andric 36*0b57cec5SDimitry Andric namespace { 37*0b57cec5SDimitry Andric 38*0b57cec5SDimitry Andric /// An abstract class that takes closures and runs them asynchronously. 39*0b57cec5SDimitry Andric class Executor { 40*0b57cec5SDimitry Andric public: 41*0b57cec5SDimitry Andric virtual ~Executor() = default; 42*0b57cec5SDimitry Andric virtual void add(std::function<void()> func) = 0; 43*0b57cec5SDimitry Andric 44*0b57cec5SDimitry Andric static Executor *getDefaultExecutor(); 45*0b57cec5SDimitry Andric }; 46*0b57cec5SDimitry Andric 47*0b57cec5SDimitry Andric /// An implementation of an Executor that runs closures on a thread pool 48*0b57cec5SDimitry Andric /// in filo order. 49*0b57cec5SDimitry Andric class ThreadPoolExecutor : public Executor { 50*0b57cec5SDimitry Andric public: 515ffd83dbSDimitry Andric explicit ThreadPoolExecutor(ThreadPoolStrategy S = hardware_concurrency()) { 525ffd83dbSDimitry Andric unsigned ThreadCount = S.compute_thread_count(); 53*0b57cec5SDimitry Andric // Spawn all but one of the threads in another thread as spawning threads 54*0b57cec5SDimitry Andric // can take a while. 55480093f4SDimitry Andric Threads.reserve(ThreadCount); 56480093f4SDimitry Andric Threads.resize(1); 57480093f4SDimitry Andric std::lock_guard<std::mutex> Lock(Mutex); 585ffd83dbSDimitry Andric Threads[0] = std::thread([this, ThreadCount, S] { 595ffd83dbSDimitry Andric for (unsigned I = 1; I < ThreadCount; ++I) { 605ffd83dbSDimitry Andric Threads.emplace_back([=] { work(S, I); }); 61480093f4SDimitry Andric if (Stop) 62480093f4SDimitry Andric break; 63*0b57cec5SDimitry Andric } 64480093f4SDimitry Andric ThreadsCreated.set_value(); 655ffd83dbSDimitry Andric work(S, 0); 66480093f4SDimitry Andric }); 67480093f4SDimitry Andric } 68480093f4SDimitry Andric 69480093f4SDimitry Andric void stop() { 70480093f4SDimitry Andric { 71480093f4SDimitry Andric std::lock_guard<std::mutex> Lock(Mutex); 72480093f4SDimitry Andric if (Stop) 73480093f4SDimitry Andric return; 74480093f4SDimitry Andric Stop = true; 75480093f4SDimitry Andric } 76480093f4SDimitry Andric Cond.notify_all(); 77480093f4SDimitry Andric ThreadsCreated.get_future().wait(); 78*0b57cec5SDimitry Andric } 79*0b57cec5SDimitry Andric 80*0b57cec5SDimitry Andric ~ThreadPoolExecutor() override { 81480093f4SDimitry Andric stop(); 82480093f4SDimitry Andric std::thread::id CurrentThreadId = std::this_thread::get_id(); 83480093f4SDimitry Andric for (std::thread &T : Threads) 84480093f4SDimitry Andric if (T.get_id() == CurrentThreadId) 85480093f4SDimitry Andric T.detach(); 86480093f4SDimitry Andric else 87480093f4SDimitry Andric T.join(); 88*0b57cec5SDimitry Andric } 89*0b57cec5SDimitry Andric 905ffd83dbSDimitry Andric struct Creator { 915ffd83dbSDimitry Andric static void *call() { return new ThreadPoolExecutor(strategy); } 925ffd83dbSDimitry Andric }; 93480093f4SDimitry Andric struct Deleter { 94480093f4SDimitry Andric static void call(void *Ptr) { ((ThreadPoolExecutor *)Ptr)->stop(); } 95480093f4SDimitry Andric }; 96480093f4SDimitry Andric 97*0b57cec5SDimitry Andric void add(std::function<void()> F) override { 98480093f4SDimitry Andric { 99480093f4SDimitry Andric std::lock_guard<std::mutex> Lock(Mutex); 10081ad6265SDimitry Andric WorkStack.push(std::move(F)); 101480093f4SDimitry Andric } 102*0b57cec5SDimitry Andric Cond.notify_one(); 103*0b57cec5SDimitry Andric } 104*0b57cec5SDimitry Andric 105*0b57cec5SDimitry Andric private: 1065ffd83dbSDimitry Andric void work(ThreadPoolStrategy S, unsigned ThreadID) { 107bdd1243dSDimitry Andric threadIndex = ThreadID; 1085ffd83dbSDimitry Andric S.apply_thread_strategy(ThreadID); 109*0b57cec5SDimitry Andric while (true) { 110*0b57cec5SDimitry Andric std::unique_lock<std::mutex> Lock(Mutex); 111*0b57cec5SDimitry Andric Cond.wait(Lock, [&] { return Stop || !WorkStack.empty(); }); 112*0b57cec5SDimitry Andric if (Stop) 113*0b57cec5SDimitry Andric break; 11481ad6265SDimitry Andric auto Task = std::move(WorkStack.top()); 115*0b57cec5SDimitry Andric WorkStack.pop(); 116*0b57cec5SDimitry Andric Lock.unlock(); 117*0b57cec5SDimitry Andric Task(); 118*0b57cec5SDimitry Andric } 119*0b57cec5SDimitry Andric } 120*0b57cec5SDimitry Andric 121*0b57cec5SDimitry Andric std::atomic<bool> Stop{false}; 122*0b57cec5SDimitry Andric std::stack<std::function<void()>> WorkStack; 123*0b57cec5SDimitry Andric std::mutex Mutex; 124*0b57cec5SDimitry Andric std::condition_variable Cond; 125480093f4SDimitry Andric std::promise<void> ThreadsCreated; 126480093f4SDimitry Andric std::vector<std::thread> Threads; 127*0b57cec5SDimitry Andric }; 128*0b57cec5SDimitry Andric 129*0b57cec5SDimitry Andric Executor *Executor::getDefaultExecutor() { 130480093f4SDimitry Andric // The ManagedStatic enables the ThreadPoolExecutor to be stopped via 131480093f4SDimitry Andric // llvm_shutdown() which allows a "clean" fast exit, e.g. via _exit(). This 132480093f4SDimitry Andric // stops the thread pool and waits for any worker thread creation to complete 133480093f4SDimitry Andric // but does not wait for the threads to finish. The wait for worker thread 134480093f4SDimitry Andric // creation to complete is important as it prevents intermittent crashes on 135480093f4SDimitry Andric // Windows due to a race condition between thread creation and process exit. 136480093f4SDimitry Andric // 137480093f4SDimitry Andric // The ThreadPoolExecutor will only be destroyed when the static unique_ptr to 138480093f4SDimitry Andric // it is destroyed, i.e. in a normal full exit. The ThreadPoolExecutor 139480093f4SDimitry Andric // destructor ensures it has been stopped and waits for worker threads to 140480093f4SDimitry Andric // finish. The wait is important as it prevents intermittent crashes on 141480093f4SDimitry Andric // Windows when the process is doing a full exit. 142480093f4SDimitry Andric // 143480093f4SDimitry Andric // The Windows crashes appear to only occur with the MSVC static runtimes and 144480093f4SDimitry Andric // are more frequent with the debug static runtime. 145480093f4SDimitry Andric // 146480093f4SDimitry Andric // This also prevents intermittent deadlocks on exit with the MinGW runtime. 1475ffd83dbSDimitry Andric 1485ffd83dbSDimitry Andric static ManagedStatic<ThreadPoolExecutor, ThreadPoolExecutor::Creator, 149480093f4SDimitry Andric ThreadPoolExecutor::Deleter> 150480093f4SDimitry Andric ManagedExec; 151480093f4SDimitry Andric static std::unique_ptr<ThreadPoolExecutor> Exec(&(*ManagedExec)); 152480093f4SDimitry Andric return Exec.get(); 153*0b57cec5SDimitry Andric } 1548bcb0991SDimitry Andric } // namespace 155bdd1243dSDimitry Andric } // namespace detail 156bdd1243dSDimitry Andric #endif 157*0b57cec5SDimitry Andric 158*0b57cec5SDimitry Andric static std::atomic<int> TaskGroupInstances; 159*0b57cec5SDimitry Andric 160*0b57cec5SDimitry Andric // Latch::sync() called by the dtor may cause one thread to block. If is a dead 161*0b57cec5SDimitry Andric // lock if all threads in the default executor are blocked. To prevent the dead 162*0b57cec5SDimitry Andric // lock, only allow the first TaskGroup to run tasks parallelly. In the scenario 163*0b57cec5SDimitry Andric // of nested parallel_for_each(), only the outermost one runs parallelly. 164*0b57cec5SDimitry Andric TaskGroup::TaskGroup() : Parallel(TaskGroupInstances++ == 0) {} 165349cc55cSDimitry Andric TaskGroup::~TaskGroup() { 166349cc55cSDimitry Andric // We must ensure that all the workloads have finished before decrementing the 167349cc55cSDimitry Andric // instances count. 168349cc55cSDimitry Andric L.sync(); 169349cc55cSDimitry Andric --TaskGroupInstances; 170349cc55cSDimitry Andric } 171*0b57cec5SDimitry Andric 172*0b57cec5SDimitry Andric void TaskGroup::spawn(std::function<void()> F) { 173bdd1243dSDimitry Andric #if LLVM_ENABLE_THREADS 174*0b57cec5SDimitry Andric if (Parallel) { 175*0b57cec5SDimitry Andric L.inc(); 176bdd1243dSDimitry Andric detail::Executor::getDefaultExecutor()->add([&, F = std::move(F)] { 177*0b57cec5SDimitry Andric F(); 178*0b57cec5SDimitry Andric L.dec(); 179*0b57cec5SDimitry Andric }); 180bdd1243dSDimitry Andric return; 181bdd1243dSDimitry Andric } 182bdd1243dSDimitry Andric #endif 183*0b57cec5SDimitry Andric F(); 184*0b57cec5SDimitry Andric } 185*0b57cec5SDimitry Andric 186bdd1243dSDimitry Andric void TaskGroup::execute(std::function<void()> F) { 187bdd1243dSDimitry Andric if (parallel::strategy.ThreadsRequested == 1) 188bdd1243dSDimitry Andric F(); 189bdd1243dSDimitry Andric else 190bdd1243dSDimitry Andric spawn(F); 191bdd1243dSDimitry Andric } 192*0b57cec5SDimitry Andric } // namespace parallel 193*0b57cec5SDimitry Andric } // namespace llvm 19404eeddc0SDimitry Andric 19581ad6265SDimitry Andric void llvm::parallelFor(size_t Begin, size_t End, 19604eeddc0SDimitry Andric llvm::function_ref<void(size_t)> Fn) { 19704eeddc0SDimitry Andric // If we have zero or one items, then do not incur the overhead of spinning up 19804eeddc0SDimitry Andric // a task group. They are surprisingly expensive, and because they do not 19904eeddc0SDimitry Andric // support nested parallelism, a single entry task group can block parallel 20004eeddc0SDimitry Andric // execution underneath them. 20104eeddc0SDimitry Andric #if LLVM_ENABLE_THREADS 20204eeddc0SDimitry Andric auto NumItems = End - Begin; 20304eeddc0SDimitry Andric if (NumItems > 1 && parallel::strategy.ThreadsRequested != 1) { 20404eeddc0SDimitry Andric // Limit the number of tasks to MaxTasksPerGroup to limit job scheduling 20504eeddc0SDimitry Andric // overhead on large inputs. 20604eeddc0SDimitry Andric auto TaskSize = NumItems / parallel::detail::MaxTasksPerGroup; 20704eeddc0SDimitry Andric if (TaskSize == 0) 20804eeddc0SDimitry Andric TaskSize = 1; 20904eeddc0SDimitry Andric 210bdd1243dSDimitry Andric parallel::TaskGroup TG; 21104eeddc0SDimitry Andric for (; Begin + TaskSize < End; Begin += TaskSize) { 21204eeddc0SDimitry Andric TG.spawn([=, &Fn] { 21304eeddc0SDimitry Andric for (size_t I = Begin, E = Begin + TaskSize; I != E; ++I) 21404eeddc0SDimitry Andric Fn(I); 21504eeddc0SDimitry Andric }); 21604eeddc0SDimitry Andric } 2171ac55f4cSDimitry Andric if (Begin != End) { 2181ac55f4cSDimitry Andric TG.spawn([=, &Fn] { 2191ac55f4cSDimitry Andric for (size_t I = Begin; I != End; ++I) 2201ac55f4cSDimitry Andric Fn(I); 2211ac55f4cSDimitry Andric }); 2221ac55f4cSDimitry Andric } 22304eeddc0SDimitry Andric return; 22404eeddc0SDimitry Andric } 22504eeddc0SDimitry Andric #endif 22604eeddc0SDimitry Andric 22704eeddc0SDimitry Andric for (; Begin != End; ++Begin) 22804eeddc0SDimitry Andric Fn(Begin); 22904eeddc0SDimitry Andric } 230