10#include "llvm/Config/llvm-config.h"
24#if LLVM_ENABLE_THREADS
27static thread_local unsigned threadIndex = UINT_MAX;
31thread_local unsigned threadIndex = UINT_MAX;
41 virtual ~Executor() =
default;
42 virtual void add(std::function<
void()> func,
bool Sequential =
false) = 0;
45 static Executor *getDefaultExecutor();
50class ThreadPoolExecutor :
public Executor {
56 Threads.reserve(ThreadCount);
58 std::lock_guard<std::mutex> Lock(
Mutex);
61 auto &Thread0 = Threads[0];
62 Thread0 = std::thread([
this, S] {
63 for (
unsigned I = 1;
I < ThreadCount; ++
I) {
64 Threads.emplace_back([=] { work(S,
I); });
68 ThreadsCreated.set_value();
75 std::lock_guard<std::mutex> Lock(
Mutex);
81 ThreadsCreated.get_future().wait();
84 ~ThreadPoolExecutor()
override {
86 std::thread::id CurrentThreadId = std::this_thread::get_id();
87 for (std::thread &
T : Threads)
88 if (
T.get_id() == CurrentThreadId)
95 static void *call() {
return new ThreadPoolExecutor(
strategy); }
98 static void call(
void *
Ptr) { ((ThreadPoolExecutor *)
Ptr)->stop(); }
101 void add(std::function<
void()>
F,
bool Sequential =
false)
override {
103 std::lock_guard<std::mutex> Lock(
Mutex);
105 WorkQueueSequential.emplace_front(std::move(
F));
107 WorkQueue.emplace_back(std::move(
F));
115 bool hasSequentialTasks()
const {
116 return !WorkQueueSequential.empty() && !SequentialQueueIsLocked;
119 bool hasGeneralTasks()
const {
return !WorkQueue.empty(); }
122 threadIndex = ThreadID;
125 std::unique_lock<std::mutex> Lock(
Mutex);
126 Cond.wait(Lock, [&] {
127 return Stop || hasGeneralTasks() || hasSequentialTasks();
131 bool Sequential = hasSequentialTasks();
133 SequentialQueueIsLocked =
true;
135 assert(hasGeneralTasks());
137 auto &Queue = Sequential ? WorkQueueSequential : WorkQueue;
138 auto Task = std::move(Queue.back());
143 SequentialQueueIsLocked =
false;
147 std::atomic<bool> Stop{
false};
148 std::atomic<bool> SequentialQueueIsLocked{
false};
149 std::deque<std::function<void()>> WorkQueue;
150 std::deque<std::function<void()>> WorkQueueSequential;
152 std::condition_variable
Cond;
153 std::promise<void> ThreadsCreated;
154 std::vector<std::thread> Threads;
155 unsigned ThreadCount;
158Executor *Executor::getDefaultExecutor() {
178 static ManagedStatic<ThreadPoolExecutor, ThreadPoolExecutor::Creator,
179 ThreadPoolExecutor::Deleter>
181 static std::unique_ptr<ThreadPoolExecutor> Exec(&(*ManagedExec));
188 static ThreadPoolExecutor Exec(
strategy);
196 return detail::Executor::getDefaultExecutor()->getThreadCount();
204TaskGroup::TaskGroup()
205#if LLVM_ENABLE_THREADS
207 (threadIndex == UINT_MAX)) {}
211TaskGroup::~TaskGroup() {
217void TaskGroup::spawn(std::function<
void()>
F,
bool Sequential) {
218#if LLVM_ENABLE_THREADS
221 detail::Executor::getDefaultExecutor()->add(
222 [&,
F = std::move(
F)] {
238#if LLVM_ENABLE_THREADS
239 if (parallel::strategy.ThreadsRequested != 1) {
240 auto NumItems =
End - Begin;
243 auto TaskSize = NumItems / parallel::detail::MaxTasksPerGroup;
248 for (; Begin + TaskSize <
End; Begin += TaskSize) {
250 for (
size_t I = Begin,
E = Begin + TaskSize;
I !=
E; ++
I)
256 for (
size_t I = Begin;
I !=
End; ++
I)
264 for (; Begin !=
End; ++Begin)
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
const SmallVectorImpl< MachineOperand > & Cond
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
ManagedStatic - This transparently changes the behavior of global statics to be lazily constructed on...
This tells how a thread pool will be used.
void apply_thread_strategy(unsigned ThreadPoolNum) const
Assign the current thread to an ideal hardware CPU or NUMA node.
unsigned compute_thread_count() const
Retrieves the max available threads for the current strategy.
An efficient, type-erasing, non-owning reference to a callable.
void spawn(std::function< void()> f, bool Sequential=false)
ThreadPoolStrategy strategy
unsigned getThreadIndex()
This is an optimization pass for GlobalISel generic memory operations.
void parallelFor(size_t Begin, size_t End, function_ref< void(size_t)> Fn)