10b57cec5SDimitry Andric //===- llvm/Support/Parallel.cpp - Parallel algorithms --------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 90b57cec5SDimitry Andric #include "llvm/Support/Parallel.h" 100b57cec5SDimitry Andric #include "llvm/Config/llvm-config.h" 11480093f4SDimitry Andric #include "llvm/Support/ManagedStatic.h" 120b57cec5SDimitry Andric #include "llvm/Support/Threading.h" 130b57cec5SDimitry Andric 140b57cec5SDimitry Andric #include <atomic> 15*fe013be4SDimitry Andric #include <deque> 16480093f4SDimitry Andric #include <future> 170b57cec5SDimitry Andric #include <thread> 18480093f4SDimitry Andric #include <vector> 190b57cec5SDimitry Andric 205ffd83dbSDimitry Andric llvm::ThreadPoolStrategy llvm::parallel::strategy; 215ffd83dbSDimitry Andric 220b57cec5SDimitry Andric namespace llvm { 230b57cec5SDimitry Andric namespace parallel { 24bdd1243dSDimitry Andric #if LLVM_ENABLE_THREADS 25bdd1243dSDimitry Andric 26bdd1243dSDimitry Andric #ifdef _WIN32 27*fe013be4SDimitry Andric static thread_local unsigned threadIndex = UINT_MAX; 28bdd1243dSDimitry Andric 29*fe013be4SDimitry Andric unsigned getThreadIndex() { GET_THREAD_INDEX_IMPL; } 30bdd1243dSDimitry Andric #else 31*fe013be4SDimitry Andric thread_local unsigned threadIndex = UINT_MAX; 32bdd1243dSDimitry Andric #endif 33bdd1243dSDimitry Andric 340b57cec5SDimitry Andric namespace detail { 350b57cec5SDimitry Andric 360b57cec5SDimitry Andric namespace { 370b57cec5SDimitry Andric 380b57cec5SDimitry Andric /// An abstract class that takes closures and runs them asynchronously. 390b57cec5SDimitry Andric class Executor { 400b57cec5SDimitry Andric public: 410b57cec5SDimitry Andric virtual ~Executor() = default; 42*fe013be4SDimitry Andric virtual void add(std::function<void()> func, bool Sequential = false) = 0; 43*fe013be4SDimitry Andric virtual size_t getThreadCount() const = 0; 440b57cec5SDimitry Andric 450b57cec5SDimitry Andric static Executor *getDefaultExecutor(); 460b57cec5SDimitry Andric }; 470b57cec5SDimitry Andric 480b57cec5SDimitry Andric /// An implementation of an Executor that runs closures on a thread pool 490b57cec5SDimitry Andric /// in filo order. 500b57cec5SDimitry Andric class ThreadPoolExecutor : public Executor { 510b57cec5SDimitry Andric public: 525ffd83dbSDimitry Andric explicit ThreadPoolExecutor(ThreadPoolStrategy S = hardware_concurrency()) { 53*fe013be4SDimitry Andric ThreadCount = S.compute_thread_count(); 540b57cec5SDimitry Andric // Spawn all but one of the threads in another thread as spawning threads 550b57cec5SDimitry Andric // can take a while. 56480093f4SDimitry Andric Threads.reserve(ThreadCount); 57480093f4SDimitry Andric Threads.resize(1); 58480093f4SDimitry Andric std::lock_guard<std::mutex> Lock(Mutex); 59*fe013be4SDimitry Andric // Use operator[] before creating the thread to avoid data race in .size() 60*fe013be4SDimitry Andric // in “safe libc++” mode. 61*fe013be4SDimitry Andric auto &Thread0 = Threads[0]; 62*fe013be4SDimitry Andric Thread0 = std::thread([this, S] { 635ffd83dbSDimitry Andric for (unsigned I = 1; I < ThreadCount; ++I) { 645ffd83dbSDimitry Andric Threads.emplace_back([=] { work(S, I); }); 65480093f4SDimitry Andric if (Stop) 66480093f4SDimitry Andric break; 670b57cec5SDimitry Andric } 68480093f4SDimitry Andric ThreadsCreated.set_value(); 695ffd83dbSDimitry Andric work(S, 0); 70480093f4SDimitry Andric }); 71480093f4SDimitry Andric } 72480093f4SDimitry Andric 73480093f4SDimitry Andric void stop() { 74480093f4SDimitry Andric { 75480093f4SDimitry Andric std::lock_guard<std::mutex> Lock(Mutex); 76480093f4SDimitry Andric if (Stop) 77480093f4SDimitry Andric return; 78480093f4SDimitry Andric Stop = true; 79480093f4SDimitry Andric } 80480093f4SDimitry Andric Cond.notify_all(); 81480093f4SDimitry Andric ThreadsCreated.get_future().wait(); 820b57cec5SDimitry Andric } 830b57cec5SDimitry Andric 840b57cec5SDimitry Andric ~ThreadPoolExecutor() override { 85480093f4SDimitry Andric stop(); 86480093f4SDimitry Andric std::thread::id CurrentThreadId = std::this_thread::get_id(); 87480093f4SDimitry Andric for (std::thread &T : Threads) 88480093f4SDimitry Andric if (T.get_id() == CurrentThreadId) 89480093f4SDimitry Andric T.detach(); 90480093f4SDimitry Andric else 91480093f4SDimitry Andric T.join(); 920b57cec5SDimitry Andric } 930b57cec5SDimitry Andric 945ffd83dbSDimitry Andric struct Creator { 955ffd83dbSDimitry Andric static void *call() { return new ThreadPoolExecutor(strategy); } 965ffd83dbSDimitry Andric }; 97480093f4SDimitry Andric struct Deleter { 98480093f4SDimitry Andric static void call(void *Ptr) { ((ThreadPoolExecutor *)Ptr)->stop(); } 99480093f4SDimitry Andric }; 100480093f4SDimitry Andric 101*fe013be4SDimitry Andric void add(std::function<void()> F, bool Sequential = false) override { 102480093f4SDimitry Andric { 103480093f4SDimitry Andric std::lock_guard<std::mutex> Lock(Mutex); 104*fe013be4SDimitry Andric if (Sequential) 105*fe013be4SDimitry Andric WorkQueueSequential.emplace_front(std::move(F)); 106*fe013be4SDimitry Andric else 107*fe013be4SDimitry Andric WorkQueue.emplace_back(std::move(F)); 108480093f4SDimitry Andric } 1090b57cec5SDimitry Andric Cond.notify_one(); 1100b57cec5SDimitry Andric } 1110b57cec5SDimitry Andric 112*fe013be4SDimitry Andric size_t getThreadCount() const override { return ThreadCount; } 113*fe013be4SDimitry Andric 1140b57cec5SDimitry Andric private: 115*fe013be4SDimitry Andric bool hasSequentialTasks() const { 116*fe013be4SDimitry Andric return !WorkQueueSequential.empty() && !SequentialQueueIsLocked; 117*fe013be4SDimitry Andric } 118*fe013be4SDimitry Andric 119*fe013be4SDimitry Andric bool hasGeneralTasks() const { return !WorkQueue.empty(); } 120*fe013be4SDimitry Andric 1215ffd83dbSDimitry Andric void work(ThreadPoolStrategy S, unsigned ThreadID) { 122bdd1243dSDimitry Andric threadIndex = ThreadID; 1235ffd83dbSDimitry Andric S.apply_thread_strategy(ThreadID); 1240b57cec5SDimitry Andric while (true) { 1250b57cec5SDimitry Andric std::unique_lock<std::mutex> Lock(Mutex); 126*fe013be4SDimitry Andric Cond.wait(Lock, [&] { 127*fe013be4SDimitry Andric return Stop || hasGeneralTasks() || hasSequentialTasks(); 128*fe013be4SDimitry Andric }); 1290b57cec5SDimitry Andric if (Stop) 1300b57cec5SDimitry Andric break; 131*fe013be4SDimitry Andric bool Sequential = hasSequentialTasks(); 132*fe013be4SDimitry Andric if (Sequential) 133*fe013be4SDimitry Andric SequentialQueueIsLocked = true; 134*fe013be4SDimitry Andric else 135*fe013be4SDimitry Andric assert(hasGeneralTasks()); 136*fe013be4SDimitry Andric 137*fe013be4SDimitry Andric auto &Queue = Sequential ? WorkQueueSequential : WorkQueue; 138*fe013be4SDimitry Andric auto Task = std::move(Queue.back()); 139*fe013be4SDimitry Andric Queue.pop_back(); 1400b57cec5SDimitry Andric Lock.unlock(); 1410b57cec5SDimitry Andric Task(); 142*fe013be4SDimitry Andric if (Sequential) 143*fe013be4SDimitry Andric SequentialQueueIsLocked = false; 1440b57cec5SDimitry Andric } 1450b57cec5SDimitry Andric } 1460b57cec5SDimitry Andric 1470b57cec5SDimitry Andric std::atomic<bool> Stop{false}; 148*fe013be4SDimitry Andric std::atomic<bool> SequentialQueueIsLocked{false}; 149*fe013be4SDimitry Andric std::deque<std::function<void()>> WorkQueue; 150*fe013be4SDimitry Andric std::deque<std::function<void()>> WorkQueueSequential; 1510b57cec5SDimitry Andric std::mutex Mutex; 1520b57cec5SDimitry Andric std::condition_variable Cond; 153480093f4SDimitry Andric std::promise<void> ThreadsCreated; 154480093f4SDimitry Andric std::vector<std::thread> Threads; 155*fe013be4SDimitry Andric unsigned ThreadCount; 1560b57cec5SDimitry Andric }; 1570b57cec5SDimitry Andric 1580b57cec5SDimitry Andric Executor *Executor::getDefaultExecutor() { 159480093f4SDimitry Andric // The ManagedStatic enables the ThreadPoolExecutor to be stopped via 160480093f4SDimitry Andric // llvm_shutdown() which allows a "clean" fast exit, e.g. via _exit(). This 161480093f4SDimitry Andric // stops the thread pool and waits for any worker thread creation to complete 162480093f4SDimitry Andric // but does not wait for the threads to finish. The wait for worker thread 163480093f4SDimitry Andric // creation to complete is important as it prevents intermittent crashes on 164480093f4SDimitry Andric // Windows due to a race condition between thread creation and process exit. 165480093f4SDimitry Andric // 166480093f4SDimitry Andric // The ThreadPoolExecutor will only be destroyed when the static unique_ptr to 167480093f4SDimitry Andric // it is destroyed, i.e. in a normal full exit. The ThreadPoolExecutor 168480093f4SDimitry Andric // destructor ensures it has been stopped and waits for worker threads to 169480093f4SDimitry Andric // finish. The wait is important as it prevents intermittent crashes on 170480093f4SDimitry Andric // Windows when the process is doing a full exit. 171480093f4SDimitry Andric // 172480093f4SDimitry Andric // The Windows crashes appear to only occur with the MSVC static runtimes and 173480093f4SDimitry Andric // are more frequent with the debug static runtime. 174480093f4SDimitry Andric // 175480093f4SDimitry Andric // This also prevents intermittent deadlocks on exit with the MinGW runtime. 1765ffd83dbSDimitry Andric 1775ffd83dbSDimitry Andric static ManagedStatic<ThreadPoolExecutor, ThreadPoolExecutor::Creator, 178480093f4SDimitry Andric ThreadPoolExecutor::Deleter> 179480093f4SDimitry Andric ManagedExec; 180480093f4SDimitry Andric static std::unique_ptr<ThreadPoolExecutor> Exec(&(*ManagedExec)); 181480093f4SDimitry Andric return Exec.get(); 1820b57cec5SDimitry Andric } 1838bcb0991SDimitry Andric } // namespace 184bdd1243dSDimitry Andric } // namespace detail 1850b57cec5SDimitry Andric 186*fe013be4SDimitry Andric size_t getThreadCount() { 187*fe013be4SDimitry Andric return detail::Executor::getDefaultExecutor()->getThreadCount(); 188*fe013be4SDimitry Andric } 189*fe013be4SDimitry Andric #endif 1900b57cec5SDimitry Andric 1910b57cec5SDimitry Andric // Latch::sync() called by the dtor may cause one thread to block. If is a dead 1920b57cec5SDimitry Andric // lock if all threads in the default executor are blocked. To prevent the dead 193*fe013be4SDimitry Andric // lock, only allow the root TaskGroup to run tasks parallelly. In the scenario 1940b57cec5SDimitry Andric // of nested parallel_for_each(), only the outermost one runs parallelly. 195*fe013be4SDimitry Andric TaskGroup::TaskGroup() 196*fe013be4SDimitry Andric #if LLVM_ENABLE_THREADS 197*fe013be4SDimitry Andric : Parallel((parallel::strategy.ThreadsRequested != 1) && 198*fe013be4SDimitry Andric (threadIndex == UINT_MAX)) {} 199*fe013be4SDimitry Andric #else 200*fe013be4SDimitry Andric : Parallel(false) {} 201*fe013be4SDimitry Andric #endif 202349cc55cSDimitry Andric TaskGroup::~TaskGroup() { 203349cc55cSDimitry Andric // We must ensure that all the workloads have finished before decrementing the 204349cc55cSDimitry Andric // instances count. 205349cc55cSDimitry Andric L.sync(); 206349cc55cSDimitry Andric } 2070b57cec5SDimitry Andric 208*fe013be4SDimitry Andric void TaskGroup::spawn(std::function<void()> F, bool Sequential) { 209bdd1243dSDimitry Andric #if LLVM_ENABLE_THREADS 2100b57cec5SDimitry Andric if (Parallel) { 2110b57cec5SDimitry Andric L.inc(); 212*fe013be4SDimitry Andric detail::Executor::getDefaultExecutor()->add( 213*fe013be4SDimitry Andric [&, F = std::move(F)] { 2140b57cec5SDimitry Andric F(); 2150b57cec5SDimitry Andric L.dec(); 216*fe013be4SDimitry Andric }, 217*fe013be4SDimitry Andric Sequential); 218bdd1243dSDimitry Andric return; 219bdd1243dSDimitry Andric } 220bdd1243dSDimitry Andric #endif 2210b57cec5SDimitry Andric F(); 2220b57cec5SDimitry Andric } 2230b57cec5SDimitry Andric 2240b57cec5SDimitry Andric } // namespace parallel 2250b57cec5SDimitry Andric } // namespace llvm 22604eeddc0SDimitry Andric 22781ad6265SDimitry Andric void llvm::parallelFor(size_t Begin, size_t End, 22804eeddc0SDimitry Andric llvm::function_ref<void(size_t)> Fn) { 22904eeddc0SDimitry Andric #if LLVM_ENABLE_THREADS 230*fe013be4SDimitry Andric if (parallel::strategy.ThreadsRequested != 1) { 23104eeddc0SDimitry Andric auto NumItems = End - Begin; 23204eeddc0SDimitry Andric // Limit the number of tasks to MaxTasksPerGroup to limit job scheduling 23304eeddc0SDimitry Andric // overhead on large inputs. 23404eeddc0SDimitry Andric auto TaskSize = NumItems / parallel::detail::MaxTasksPerGroup; 23504eeddc0SDimitry Andric if (TaskSize == 0) 23604eeddc0SDimitry Andric TaskSize = 1; 23704eeddc0SDimitry Andric 238bdd1243dSDimitry Andric parallel::TaskGroup TG; 23904eeddc0SDimitry Andric for (; Begin + TaskSize < End; Begin += TaskSize) { 24004eeddc0SDimitry Andric TG.spawn([=, &Fn] { 24104eeddc0SDimitry Andric for (size_t I = Begin, E = Begin + TaskSize; I != E; ++I) 24204eeddc0SDimitry Andric Fn(I); 24304eeddc0SDimitry Andric }); 24404eeddc0SDimitry Andric } 2451ac55f4cSDimitry Andric if (Begin != End) { 2461ac55f4cSDimitry Andric TG.spawn([=, &Fn] { 2471ac55f4cSDimitry Andric for (size_t I = Begin; I != End; ++I) 2481ac55f4cSDimitry Andric Fn(I); 2491ac55f4cSDimitry Andric }); 2501ac55f4cSDimitry Andric } 25104eeddc0SDimitry Andric return; 25204eeddc0SDimitry Andric } 25304eeddc0SDimitry Andric #endif 25404eeddc0SDimitry Andric 25504eeddc0SDimitry Andric for (; Begin != End; ++Begin) 25604eeddc0SDimitry Andric Fn(Begin); 25704eeddc0SDimitry Andric } 258