Proteus
Programmable JIT compilation and optimization for C/C++ using LLVM
Loading...
Searching...
No Matches
CompilerAsync.hpp
Go to the documentation of this file.
1#ifndef PROTEUS_ASYNC_COMPILER_HPP
2#define PROTEUS_ASYNC_COMPILER_HPP
3
4#include <condition_variable>
5#include <deque>
6#include <thread>
7
9#include "proteus/Debug.h"
10#include "proteus/Hashing.hpp"
11
12namespace proteus {
13
14using namespace llvm;
15
17public:
18 explicit CompilationResult() : IsReadyFlag{false} {}
19
22
23 CompilationResult(CompilationResult &&) noexcept = delete;
24 CompilationResult &operator=(CompilationResult &&) noexcept = delete;
25
26 bool isReady() { return IsReadyFlag; }
27
28 void set(std::unique_ptr<MemoryBuffer> ObjBuf) {
29 ResultObjBuf = std::move(ObjBuf);
30 IsReadyFlag = true;
31 }
32
33 void wait() {
34 // Busy wait until it's ready.
35 while (!IsReadyFlag) {
36 std::this_thread::yield();
37 }
38 }
39
40 std::unique_ptr<MemoryBuffer> take() { return std::move(ResultObjBuf); }
41
42private:
43 std::atomic<bool> IsReadyFlag;
44 std::unique_ptr<MemoryBuffer> ResultObjBuf;
45};
46
48public:
49 static CompilerAsync &instance(int NumThreads) {
50 static CompilerAsync Singleton{NumThreads};
51 return Singleton;
52 }
53
55 std::unique_lock Lock{Mutex};
56 // Get HashValue before the move of CT.
57 HashT HashValue = CT.getHashValue();
58 Worklist.emplace_back(std::move(CT));
59 CompilationResultMap.emplace(HashValue,
60 std::make_unique<CompilationResult>());
61 CondVar.notify_one();
62 }
63
64 void run() {
65 [[maybe_unused]] int Count = 0;
66 while (Active) {
67 std::unique_lock Lock(Mutex);
68 CondVar.wait(Lock, [this] { return !Worklist.empty() || !Active; });
69 if (!Active)
70 break;
71 if (Worklist.empty())
72 PROTEUS_FATAL_ERROR("Expected non-empty Worklist");
73 CompilationTask CT = std::move(Worklist.front());
74 Worklist.pop_front();
75 Lock.unlock();
76
77 Count++;
78 std::unique_ptr<MemoryBuffer> ObjBuf = CT.compile();
79 Lock.lock();
80 CompilationResultMap.at(CT.getHashValue())->set(std::move(ObjBuf));
81 Lock.unlock();
82 }
83
84 PROTEUS_DBG(Logger::logs("proteus")
85 << "Thread exiting! Compiled " + std::to_string(Count) + "\n");
86 }
87
89 {
90 std::unique_lock Lock{Mutex};
91 // Return if already inactive.
92 if (!Active)
93 return;
94
95 Active = false;
96 }
97 CondVar.notify_all();
98
99 for (auto &Thread : Threads)
100 Thread.join();
101
102 Threads.clear();
103 }
104
105 bool isCompilationPending(HashT HashValue) {
106 std::unique_lock Lock{Mutex};
107 return !(CompilationResultMap.find(HashValue) ==
108 CompilationResultMap.end());
109 }
110
111 std::unique_ptr<MemoryBuffer> takeCompilationResult(HashT HashValue,
112 bool BlockingWait) {
113 std::unique_lock Lock{Mutex};
114 auto It = CompilationResultMap.find(HashValue);
115 if (It == CompilationResultMap.end())
116 return nullptr;
117
118 std::unique_ptr<CompilationResult> &CRes = It->second;
119
120 if (BlockingWait) {
121 // Release the lock while waiting for the result.
122 Lock.unlock();
123 CRes->wait();
124 // Reacquire the lock for synchronized access, the result is ready.
125 Lock.lock();
126 } else {
127 if (!CRes->isReady())
128 return nullptr;
129 }
130
131 // If compilation result is ready, take ownership of the buffer, erase it
132 // from the compilation results map and move the buffer to the caller.
133 std::unique_ptr<MemoryBuffer> ObjBuf = CRes->take();
134 // Use the HashValue key as the iterator may have been invalidated by
135 // insert/emplace from another thread.
136 CompilationResultMap.erase(HashValue);
137 Lock.unlock();
138 return ObjBuf;
139 }
140
141private:
142 bool Active;
143 std::mutex Mutex;
144 std::condition_variable CondVar;
145 std::unordered_map<HashT, std::unique_ptr<CompilationResult>>
146 CompilationResultMap;
147 std::deque<CompilationTask> Worklist;
148 std::vector<std::thread> Threads;
149
150 CompilerAsync(int NumThreads) {
151 Active = true;
152 for (int I = 0; I < NumThreads; ++I)
153 Threads.emplace_back(&CompilerAsync::run, this);
154 }
155
157};
158
159} // namespace proteus
160
161#endif
#define PROTEUS_DBG(x)
Definition Debug.h:10
#define PROTEUS_FATAL_ERROR(x)
Definition Error.h:7
Definition CompilerAsync.hpp:16
CompilationResult(CompilationResult &&) noexcept=delete
void set(std::unique_ptr< MemoryBuffer > ObjBuf)
Definition CompilerAsync.hpp:28
bool isReady()
Definition CompilerAsync.hpp:26
CompilationResult(const CompilationResult &)=delete
CompilationResult & operator=(const CompilationResult &)=delete
std::unique_ptr< MemoryBuffer > take()
Definition CompilerAsync.hpp:40
void wait()
Definition CompilerAsync.hpp:33
CompilationResult()
Definition CompilerAsync.hpp:18
Definition CompilationTask.hpp:17
HashT getHashValue() const
Definition CompilationTask.hpp:102
std::unique_ptr< MemoryBuffer > compile()
Definition CompilationTask.hpp:104
Definition CompilerAsync.hpp:47
bool isCompilationPending(HashT HashValue)
Definition CompilerAsync.hpp:105
static CompilerAsync & instance(int NumThreads)
Definition CompilerAsync.hpp:49
void compile(CompilationTask &&CT)
Definition CompilerAsync.hpp:54
std::unique_ptr< MemoryBuffer > takeCompilationResult(HashT HashValue, bool BlockingWait)
Definition CompilerAsync.hpp:111
void joinAllThreads()
Definition CompilerAsync.hpp:88
void run()
Definition CompilerAsync.hpp:64
Definition Hashing.hpp:20
static llvm::raw_ostream & logs(const std::string &Name)
Definition Logger.hpp:19
Definition Helpers.h:76
Definition BuiltinsCUDA.cpp:4