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