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     }
21704eeddc0SDimitry Andric     for (; Begin != End; ++Begin)
21804eeddc0SDimitry Andric       Fn(Begin);
21904eeddc0SDimitry Andric     return;
22004eeddc0SDimitry Andric   }
22104eeddc0SDimitry Andric #endif
22204eeddc0SDimitry Andric 
22304eeddc0SDimitry Andric   for (; Begin != End; ++Begin)
22404eeddc0SDimitry Andric     Fn(Begin);
22504eeddc0SDimitry Andric }
226