1 //===- lld/Core/Parallel.h - Parallel utilities ---------------------------===//
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #ifndef LLD_CORE_PARALLEL_H
11 #define LLD_CORE_PARALLEL_H
13 #include "lld/Core/Instrumentation.h"
14 #include "lld/Core/LLVM.h"
15 #include "lld/Core/range.h"
16 #include "llvm/Support/MathExtras.h"
19 // concrt.h depends on eh.h for __uncaught_exception declaration
20 // even if we disable exceptions.
26 #include <condition_variable>
37 /// \brief Allows one or more threads to wait on a potentially unknown number of
40 /// A latch starts at \p count. inc() increments this, and dec() decrements it.
41 /// All calls to sync() will block while the count is not 0.
43 /// Calling dec() on a Latch with a count of 0 has undefined behaivor.
46 mutable std::mutex _condMut;
47 mutable std::condition_variable _cond;
50 explicit Latch(uint32_t count = 0) : _count(count) {}
54 std::unique_lock<std::mutex> lock(_condMut);
59 std::unique_lock<std::mutex> lock(_condMut);
65 std::unique_lock<std::mutex> lock(_condMut);
66 _cond.wait(lock, [&] {
72 /// \brief An implementation of future. std::future and std::promise in
73 /// old libstdc++ have a threading bug; there is a small chance that a
74 /// call of future::get throws an exception in the normal use case.
75 /// We want to use our own future implementation until we drop support
76 /// of old versions of libstdc++.
77 /// https://gcc.gnu.org/ml/gcc-patches/2014-05/msg01389.html
78 template<typename T> class Future {
80 Future() : _hasValue(false) {}
85 std::unique_lock<std::mutex> lock(_mutex);
93 std::unique_lock<std::mutex> lock(_mutex);
96 _cond.wait(lock, [&] { return _hasValue; });
104 std::condition_variable _cond;
107 /// \brief An abstract class that takes closures and runs them asynchronously.
110 virtual ~Executor() {}
111 virtual void add(std::function<void()> func) = 0;
114 /// \brief An implementation of an Executor that runs closures on a thread pool
116 class ThreadPoolExecutor : public Executor {
118 explicit ThreadPoolExecutor(unsigned threadCount =
119 std::thread::hardware_concurrency())
120 : _stop(false), _done(threadCount) {
121 // Spawn all but one of the threads in another thread as spawning threads
123 std::thread([&, threadCount] {
124 for (std::size_t i = 1; i < threadCount; ++i) {
133 ~ThreadPoolExecutor() {
134 std::unique_lock<std::mutex> lock(_mutex);
141 void add(std::function<void()> f) override {
142 std::unique_lock<std::mutex> lock(_mutex);
151 std::unique_lock<std::mutex> lock(_mutex);
152 _cond.wait(lock, [&] {
153 return _stop || !_workStack.empty();
157 auto task = _workStack.top();
165 std::atomic<bool> _stop;
166 std::stack<std::function<void()>> _workStack;
168 std::condition_variable _cond;
173 /// \brief An Executor that runs tasks via ConcRT.
174 class ConcRTExecutor : public Executor {
176 Taskish(std::function<void()> task) : _task(task) {}
178 std::function<void()> _task;
180 static void run(void *p) {
181 Taskish *self = static_cast<Taskish *>(p);
183 concurrency::Free(self);
188 virtual void add(std::function<void()> func) {
189 Concurrency::CurrentScheduler::ScheduleTask(Taskish::run,
190 new (concurrency::Alloc(sizeof(Taskish))) Taskish(func));
194 inline Executor *getDefaultExecutor() {
195 static ConcRTExecutor exec;
199 inline Executor *getDefaultExecutor() {
200 static ThreadPoolExecutor exec;
205 /// \brief Allows launching a number of tasks and waiting for them to finish
206 /// either explicitly via sync() or implicitly on destruction.
211 void spawn(std::function<void()> f) {
213 getDefaultExecutor()->add([&, f] {
219 void sync() const { _latch.sync(); }
223 // Use ppl parallel_sort on Windows.
224 template <class RandomAccessIterator, class Comp>
226 RandomAccessIterator start, RandomAccessIterator end,
227 const Comp &comp = std::less<
228 typename std::iterator_traits<RandomAccessIterator>::value_type>()) {
229 concurrency::parallel_sort(start, end, comp);
233 const ptrdiff_t minParallelSize = 1024;
235 /// \brief Inclusive median.
236 template <class RandomAccessIterator, class Comp>
237 RandomAccessIterator medianOf3(RandomAccessIterator start,
238 RandomAccessIterator end, const Comp &comp) {
239 RandomAccessIterator mid = start + (std::distance(start, end) / 2);
240 return comp(*start, *(end - 1))
241 ? (comp(*mid, *(end - 1)) ? (comp(*start, *mid) ? mid : start)
243 : (comp(*mid, *start) ? (comp(*(end - 1), *mid) ? mid : end - 1)
247 template <class RandomAccessIterator, class Comp>
248 void parallel_quick_sort(RandomAccessIterator start, RandomAccessIterator end,
249 const Comp &comp, TaskGroup &tg, size_t depth) {
250 // Do a sequential sort for small inputs.
251 if (std::distance(start, end) < detail::minParallelSize || depth == 0) {
252 std::sort(start, end, comp);
257 auto pivot = medianOf3(start, end, comp);
258 // Move pivot to end.
259 std::swap(*(end - 1), *pivot);
260 pivot = std::partition(start, end - 1, [&comp, end](decltype(*start) v) {
261 return comp(v, *(end - 1));
263 // Move pivot to middle of partition.
264 std::swap(*pivot, *(end - 1));
267 tg.spawn([=, &comp, &tg] {
268 parallel_quick_sort(start, pivot, comp, tg, depth - 1);
270 parallel_quick_sort(pivot + 1, end, comp, tg, depth - 1);
274 template <class RandomAccessIterator, class Comp>
276 RandomAccessIterator start, RandomAccessIterator end,
277 const Comp &comp = std::less<
278 typename std::iterator_traits<RandomAccessIterator>::value_type>()) {
280 detail::parallel_quick_sort(start, end, comp, tg,
281 llvm::Log2_64(std::distance(start, end)) + 1);
285 template <class T> void parallel_sort(T *start, T *end) {
286 parallel_sort(start, end, std::less<T>());
290 // Use ppl parallel_for_each on Windows.
291 template <class Iterator, class Func>
292 void parallel_for_each(Iterator begin, Iterator end, Func func) {
293 concurrency::parallel_for_each(begin, end, func);
296 template <class Iterator, class Func>
297 void parallel_for_each(Iterator begin, Iterator end, Func func) {
299 ptrdiff_t taskSize = 1024;
300 while (taskSize <= std::distance(begin, end)) {
301 tg.spawn([=, &func] { std::for_each(begin, begin + taskSize, func); });
304 std::for_each(begin, end, func);
307 } // end namespace lld