llvm.org GIT mirror llvm / 3c1ec57
[Support] Move Parallel algorithms from LLD to LLVM. Differential Revision: https://reviews.llvm.org/D33024 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@302748 91177308-0d34-0410-b5e6-96231b3b80d8 Zachary Turner 3 years ago
5 changed file(s) with 437 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 //===- llvm/Support/Parallel.h - Parallel algorithms ----------------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #ifndef LLVM_SUPPORT_PARALLEL_H
10 #define LLVM_SUPPORT_PARALLEL_H
11
12 #include "llvm/ADT/STLExtras.h"
13 #include "llvm/Config/llvm-config.h"
14 #include "llvm/Support/MathExtras.h"
15
16 #include
17 #include
18 #include
19 #include
20
21 #if defined(_MSC_VER) && LLVM_ENABLE_THREADS
22 #pragma warning(push)
23 #pragma warning(disable : 4530)
24 #include
25 #include
26 #pragma warning(pop)
27 #endif
28
29 namespace llvm {
30
31 namespace detail {
32 class Latch {
33 uint32_t Count;
34 mutable std::mutex Mutex;
35 mutable std::condition_variable Cond;
36
37 public:
38 explicit Latch(uint32_t count = 0) : Count(Count) {}
39 ~Latch() { sync(); }
40
41 void inc() {
42 std::unique_lock lock(Mutex);
43 ++Count;
44 }
45
46 void dec() {
47 std::unique_lock lock(Mutex);
48 if (--Count == 0)
49 Cond.notify_all();
50 }
51
52 void sync() const {
53 std::unique_lock lock(Mutex);
54 Cond.wait(lock, [&] { return Count == 0; });
55 }
56 };
57
58 class TaskGroup {
59 Latch L;
60
61 public:
62 void spawn(std::function f);
63
64 void sync() const { L.sync(); }
65 };
66 }
67
68 namespace parallel {
69 struct sequential_execution_policy {};
70 struct parallel_execution_policy {};
71
72 template
73 struct is_execution_policy
74 : public std::integral_constant<
75 bool, llvm::is_one_of
76 parallel_execution_policy>::value> {};
77
78 constexpr sequential_execution_policy seq{};
79 constexpr parallel_execution_policy par{};
80
81 namespace detail {
82
83 #if LLVM_ENABLE_THREADS
84
85 #if defined(_MSC_VER)
86 template
87 void parallel_sort(RandomAccessIterator Start, RandomAccessIterator End,
88 const Comparator &Comp) {
89 concurrency::parallel_sort(Start, End, Comp);
90 }
91 template
92 void parallel_for_each(IterTy Begin, IterTy End, FuncTy Fn) {
93 concurrency::parallel_for_each(Begin, End, Fn);
94 }
95
96 template
97 void parallel_for_each_n(IndexTy Begin, IndexTy End, FuncTy Fn) {
98 concurrency::parallel_for(Begin, End, Fn);
99 }
100
101 #else
102 const ptrdiff_t MinParallelSize = 1024;
103
104 /// \brief Inclusive median.
105 template
106 RandomAccessIterator medianOf3(RandomAccessIterator Start,
107 RandomAccessIterator End,
108 const Comparator &Comp) {
109 RandomAccessIterator Mid = Start + (std::distance(Start, End) / 2);
110 return Comp(*Start, *(End - 1))
111 ? (Comp(*Mid, *(End - 1)) ? (Comp(*Start, *Mid) ? Mid : Start)
112 : End - 1)
113 : (Comp(*Mid, *Start) ? (Comp(*(End - 1), *Mid) ? Mid : End - 1)
114 : Start);
115 }
116
117 template
118 void parallel_quick_sort(RandomAccessIterator Start, RandomAccessIterator End,
119 const Comparator &Comp, TaskGroup &TG, size_t Depth) {
120 // Do a sequential sort for small inputs.
121 if (std::distance(Start, End) < detail::MinParallelSize || Depth == 0) {
122 std::sort(Start, End, Comp);
123 return;
124 }
125
126 // Partition.
127 auto Pivot = medianOf3(Start, End, Comp);
128 // Move Pivot to End.
129 std::swap(*(End - 1), *Pivot);
130 Pivot = std::partition(Start, End - 1, [&Comp, End](decltype(*Start) V) {
131 return Comp(V, *(End - 1));
132 });
133 // Move Pivot to middle of partition.
134 std::swap(*Pivot, *(End - 1));
135
136 // Recurse.
137 TG.spawn([=, &Comp, &TG] {
138 parallel_quick_sort(Start, Pivot, Comp, TG, Depth - 1);
139 });
140 parallel_quick_sort(Pivot + 1, End, Comp, TG, Depth - 1);
141 }
142
143 template
144 void parallel_sort(RandomAccessIterator Start, RandomAccessIterator End,
145 const Comparator &Comp) {
146 TaskGroup TG;
147 parallel_quick_sort(Start, End, Comp, TG,
148 llvm::Log2_64(std::distance(Start, End)) + 1);
149 }
150
151 template
152 void parallel_for_each(IterTy Begin, IterTy End, FuncTy Fn) {
153 // TaskGroup has a relatively high overhead, so we want to reduce
154 // the number of spawn() calls. We'll create up to 1024 tasks here.
155 // (Note that 1024 is an arbitrary number. This code probably needs
156 // improving to take the number of available cores into account.)
157 ptrdiff_t TaskSize = std::distance(Begin, End) / 1024;
158 if (TaskSize == 0)
159 TaskSize = 1;
160
161 TaskGroup TG;
162 while (TaskSize <= std::distance(Begin, End)) {
163 TG.spawn([=, &Fn] { std::for_each(Begin, Begin + TaskSize, Fn); });
164 Begin += TaskSize;
165 }
166 TG.spawn([=, &Fn] { std::for_each(Begin, End, Fn); });
167 }
168
169 template
170 void parallel_for_each_n(IndexTy Begin, IndexTy End, FuncTy Fn) {
171 ptrdiff_t TaskSize = (End - Begin) / 1024;
172 if (TaskSize == 0)
173 TaskSize = 1;
174
175 TaskGroup TG;
176 IndexTy I = Begin;
177 for (; I + TaskSize < End; I += TaskSize) {
178 TG.spawn([=, &Fn] {
179 for (IndexTy J = I, E = I + TaskSize; J != E; ++J)
180 Fn(J);
181 });
182 }
183 TG.spawn([=, &Fn] {
184 for (IndexTy J = I; J < End; ++J)
185 Fn(J);
186 });
187 }
188
189 #endif
190
191 #endif
192
193 template
194 using DefComparator =
195 std::less::value_type>;
196
197 } // namespace detail
198
199 // sequential algorithm implementations.
200 template
201 class Comparator = detail::DefComparator>
202 void sort(Policy policy, RandomAccessIterator Start, RandomAccessIterator End,
203 const Comparator &Comp = Comparator()) {
204 static_assert(is_execution_policy::value,
205 "Invalid execution policy!");
206 std::sort(Start, End, Comp);
207 }
208
209 template
210 void for_each(Policy policy, IterTy Begin, IterTy End, FuncTy Fn) {
211 static_assert(is_execution_policy::value,
212 "Invalid execution policy!");
213 std::for_each(Begin, End, Fn);
214 }
215
216 template
217 void for_each_n(Policy policy, IndexTy Begin, IndexTy End, FuncTy Fn) {
218 static_assert(is_execution_policy::value,
219 "Invalid execution policy!");
220 for (IndexTy I = Begin; I != End; ++I)
221 Fn(I);
222 }
223
224 // Parallel algorithm implementations, only available when LLVM_ENABLE_THREADS
225 // is true.
226 #if LLVM_ENABLE_THREADS
227 template
228 class Comparator = detail::DefComparator>
229 void sort(parallel_execution_policy policy, RandomAccessIterator Start,
230 RandomAccessIterator End, const Comparator &Comp = Comparator()) {
231 detail::parallel_sort(Start, End, Comp);
232 }
233
234 template
235 void for_each(parallel_execution_policy policy, IterTy Begin, IterTy End,
236 FuncTy Fn) {
237 detail::parallel_for_each(Begin, End, Fn);
238 }
239
240 template
241 void for_each_n(parallel_execution_policy policy, IndexTy Begin, IndexTy End,
242 FuncTy Fn) {
243 detail::parallel_for_each_n(Begin, End, Fn);
244 }
245 #endif
246
247 } // namespace parallel
248 } // namespace llvm
249
250 #endif // LLVM_SUPPORT_PARALLEL_H
8080 MD5.cpp
8181 NativeFormatting.cpp
8282 Options.cpp
83 Parallel.cpp
8384 PluginLoader.cpp
8485 PrettyStackTrace.cpp
8586 RandomNumberGenerator.cpp
0 //===- llvm/Support/Parallel.cpp - Parallel algorithms --------------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "llvm/Support/Parallel.h"
10 #include "llvm/Config/llvm-config.h"
11
12 #include
13 #include
14 #include
15
16 using namespace llvm;
17
18 namespace {
19
20 /// \brief An abstract class that takes closures and runs them asynchronously.
21 class Executor {
22 public:
23 virtual ~Executor() = default;
24 virtual void add(std::function func) = 0;
25
26 static Executor *getDefaultExecutor();
27 };
28
29 #if !LLVM_ENABLE_THREADS
30 class SyncExecutor : public Executor {
31 public:
32 virtual void add(std::function F) { F(); }
33 };
34
35 Executor *Executor::getDefaultExecutor() {
36 static SyncExecutor Exec;
37 return &Exec;
38 }
39
40 #elif defined(_MSC_VER)
41 /// \brief An Executor that runs tasks via ConcRT.
42 class ConcRTExecutor : public Executor {
43 struct Taskish {
44 Taskish(std::function Task) : Task(Task) {}
45
46 std::function Task;
47
48 static void run(void *P) {
49 Taskish *Self = static_cast(P);
50 Self->Task();
51 concurrency::Free(Self);
52 }
53 };
54
55 public:
56 virtual void add(std::function F) {
57 Concurrency::CurrentScheduler::ScheduleTask(
58 Taskish::run, new (concurrency::Alloc(sizeof(Taskish))) Taskish(F));
59 }
60 };
61
62 Executor *Executor::getDefaultExecutor() {
63 static ConcRTExecutor exec;
64 return &exec;
65 }
66
67 #else
68 /// \brief An implementation of an Executor that runs closures on a thread pool
69 /// in filo order.
70 class ThreadPoolExecutor : public Executor {
71 public:
72 explicit ThreadPoolExecutor(
73 unsigned ThreadCount = std::thread::hardware_concurrency())
74 : Done(ThreadCount) {
75 // Spawn all but one of the threads in another thread as spawning threads
76 // can take a while.
77 std::thread([&, ThreadCount] {
78 for (size_t i = 1; i < ThreadCount; ++i) {
79 std::thread([=] { work(); }).detach();
80 }
81 work();
82 }).detach();
83 }
84
85 ~ThreadPoolExecutor() override {
86 std::unique_lock Lock(Mutex);
87 Stop = true;
88 Lock.unlock();
89 Cond.notify_all();
90 // Wait for ~Latch.
91 }
92
93 void add(std::function F) override {
94 std::unique_lock Lock(Mutex);
95 WorkStack.push(F);
96 Lock.unlock();
97 Cond.notify_one();
98 }
99
100 private:
101 void work() {
102 while (true) {
103 std::unique_lock Lock(Mutex);
104 Cond.wait(Lock, [&] { return Stop || !WorkStack.empty(); });
105 if (Stop)
106 break;
107 auto Task = WorkStack.top();
108 WorkStack.pop();
109 Lock.unlock();
110 Task();
111 }
112 Done.dec();
113 }
114
115 std::atomic Stop{false};
116 std::stack> WorkStack;
117 std::mutex Mutex;
118 std::condition_variable Cond;
119 Latch Done;
120 };
121
122 Executor *Executor::getDefaultExecutor() {
123 static ThreadPoolExecutor exec;
124 return &exec;
125 }
126 #endif
127 }
128
129 void detail::TaskGroup::spawn(std::function F) {
130 L.inc();
131 Executor::getDefaultExecutor()->add([&, F] {
132 F();
133 L.dec();
134 });
135 }
3535 MemoryBufferTest.cpp
3636 MemoryTest.cpp
3737 NativeFormatTests.cpp
38 ParallelTest.cpp
3839 Path.cpp
3940 ProcessTest.cpp
4041 ProgramTest.cpp
0 //===- llvm/unittest/Support/ParallelTest.cpp -----------------------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// \brief Parallel.h unit tests.
11 ///
12 //===----------------------------------------------------------------------===//
13
14 #include "llvm/Support/Parallel.h"
15 #include "gtest/gtest.h"
16 #include
17 #include
18
19 uint32_t array[1024 * 1024];
20
21 using namespace llvm;
22
23 TEST(Parallel, sort) {
24 std::mt19937 randEngine;
25 std::uniform_int_distribution dist;
26
27 for (auto &i : array)
28 i = dist(randEngine);
29
30 sort(parallel::par, std::begin(array), std::end(array));
31 ASSERT_TRUE(std::is_sorted(std::begin(array), std::end(array)));
32 }
33
34 TEST(Parallel, parallel_for) {
35 // We need to test the case with a TaskSize > 1. We are white-box testing
36 // here. The TaskSize is calculated as (End - Begin) / 1024 at the time of
37 // writing.
38 uint32_t range[2050];
39 std::fill(range, range + 2050, 1);
40 for_each_n(parallel::par, 0, 2049, [&range](size_t I) { ++range[I]; });
41
42 uint32_t expected[2049];
43 std::fill(expected, expected + 2049, 2);
44 ASSERT_TRUE(std::equal(range, range + 2049, expected));
45 // Check that we don't write past the end of the requested range.
46 ASSERT_EQ(range[2049], 1u);
47 }