Proteus
Programmable JIT compilation and optimization for C/C++ using LLVM
Loading...
Searching...
No Matches
Config.h
Go to the documentation of this file.
1#ifndef PROTEUS_CONFIG_H
2#define PROTEUS_CONFIG_H
3
4#include "proteus/Error.h"
6
7#include "llvm/ADT/StringMap.h"
8#include <llvm/Support/JSON.h>
9#include <llvm/Support/MemoryBuffer.h>
10
11#include <algorithm>
12#include <array>
13#include <cstdint>
14#include <limits>
15#include <sstream>
16#include <string>
17namespace proteus {
18
19enum class CodegenOption {
20 RTC,
21 Serial,
23};
24
30
31inline std::string toString(CodegenOption Option) {
32 switch (Option) {
34 return "RTC";
36 return "Serial";
38 return "Parallel";
39 default:
40 return "Unknown";
41 }
42}
43
44inline std::string toString(KernelCloneOption Option) {
45 switch (Option) {
47 return "link-clone-prune";
49 return "link-clone-light";
51 return "cross-clone";
52 default:
53 return "Unknown";
54 }
55}
56
57inline std::optional<std::string> getEnvOrDefaultString(const char *VarName) {
58
59 const char *EnvValue = std::getenv(VarName);
60 if (!EnvValue)
61 return std::nullopt;
62
63 return std::string(EnvValue);
64}
65
66inline char getEnvOrDefaultChar(const char *VarName, char Default) {
67
68 const char *EnvValue = std::getenv(VarName);
69 return EnvValue ? EnvValue[0] : Default;
70}
71
72inline bool getEnvOrDefaultBool(const char *VarName, bool Default) {
73
74 const char *EnvValue = std::getenv(VarName);
75 return EnvValue ? static_cast<bool>(std::stoi(EnvValue)) : Default;
76}
77
78inline int getEnvOrDefaultInt(const char *VarName, int Default) {
79
80 const char *EnvValue = std::getenv(VarName);
81 return EnvValue ? std::stoi(EnvValue) : Default;
82}
83
84inline CodegenOption strToCG(std::string CGstr) {
85 std::transform(CGstr.begin(), CGstr.end(), CGstr.begin(), ::tolower);
86 if (CGstr == "rtc")
87 return CodegenOption::RTC;
88 if (CGstr == "serial")
90 if (CGstr == "parallel")
92
93 reportFatalError("Unknown codegen option: " + CGstr);
94}
95
97 CodegenOption Default) {
98
99 const char *EnvValue = std::getenv(VarName);
100 if (!EnvValue)
101 return Default;
102
103 return strToCG(EnvValue);
104}
105
106template <typename T>
107T getDefaultValueFromOptional(std::optional<T> JSONValue, T Default) {
108 if (JSONValue)
109 return JSONValue.value();
110 return Default;
111}
112
113enum class TraceOption : unsigned {
114 Specialization = 0x1,
115 IRDump = 0x2,
116 KernelTrace = 0x4,
117 CacheStats = 0x8,
118};
119
120inline unsigned parseTraceConfig(const char *VarName) {
121 const char *EnvValue = std::getenv(VarName);
122 if (!EnvValue)
123 return 0;
124
125 unsigned Mask = 0;
126 std::istringstream Stream(EnvValue);
127 std::string Token;
128 while (std::getline(Stream, Token, ';')) {
129 if (Token.empty())
130 continue;
131 if (Token == "specialization")
132 Mask |= static_cast<unsigned>(TraceOption::Specialization);
133 else if (Token == "ir-dump")
134 Mask |= static_cast<unsigned>(TraceOption::IRDump);
135 else if (Token == "kernel-trace")
136 Mask |= static_cast<unsigned>(TraceOption::KernelTrace);
137 else if (Token == "cache-stats")
138 Mask |= static_cast<unsigned>(TraceOption::CacheStats);
139 else
140 reportFatalError("Unknown PROTEUS_TRACE_OUTPUT token: " + Token);
141 }
142 return Mask;
143}
144
146 KernelCloneOption Default) {
147
148 const char *EnvValue = std::getenv(VarName);
149 if (!EnvValue)
150 return Default;
151
152 std::string EnvValueStr{EnvValue};
153 std::transform(EnvValueStr.begin(), EnvValueStr.end(), EnvValueStr.begin(),
154 ::tolower);
155 if (EnvValueStr == "link-clone-prune")
157 if (EnvValueStr == "link-clone-light")
159 if (EnvValueStr == "cross-clone")
161
162 Logger::outs("proteus") << "Unknown kernel clone option " << EnvValueStr
163 << ", using default: " << toString(Default) << "\n";
164 return Default;
165}
166
168 using LaunchDim = std::array<unsigned int, 3>;
169
170 static CodegenOption getCodeGen(CodegenOption ProteusCodegen) {
171 constexpr bool SupportOnlyRTC =
172#if defined(PROTEUS_ENABLE_CUDA)
173 true;
174#else
175 false;
176#endif
177 if (SupportOnlyRTC && ProteusCodegen != CodegenOption::RTC) {
178 Logger::outs("proteus") << "Warning: Proteus supports only RTC in the "
179 "current build system configuration, "
180 "defaulting Codegen to RTC\n";
181 ProteusCodegen = CodegenOption::RTC;
182 }
183 return ProteusCodegen;
184 }
185
186 static std::optional<LaunchDim> getTunedDim(const llvm::json::Object &Config,
187 llvm::StringRef Key) {
188 const auto *Value = Config.get(Key);
189 if (!Value)
190 return std::nullopt;
191
192 const auto *Object = Value->getAsObject();
193 if (!Object)
194 reportFatalError("Invalid tuned " + Key.str() +
195 ": expected an object with x, y, z");
196
197 LaunchDim Dim;
198 const char *Axes[] = {"x", "y", "z"};
199 for (unsigned I = 0; I < Dim.size(); ++I) {
200 const char *Axis = Axes[I];
201 auto Coordinate = Object->getInteger(Axis);
202 if (!Coordinate || *Coordinate <= 0 ||
203 static_cast<uint64_t>(*Coordinate) >
204 std::numeric_limits<unsigned int>::max())
205 reportFatalError("Invalid tuned " + Key.str() + "." + Axis +
206 ": expected a positive 32-bit integer");
207 Dim[I] = static_cast<unsigned int>(*Coordinate);
208 }
209 return Dim;
210 }
211
212 std::optional<const std::string> ProteusOptPipeline;
213 CodegenOption ProteusCodegen;
214 bool ProteusSpecializeArgs;
215 bool ProteusSpecializeLaunchBounds;
216 bool ProteusSpecializeDims;
217 bool ProteusSpecializeDimsRange;
218 char ProteusOptLevel;
219 int ProteusCodeGenOptLevel;
220 int TunedMaxThreads;
221 int MinBlocksPerSM;
222 std::optional<LaunchDim> TunedGridDim;
223 std::optional<LaunchDim> TunedBlockDim;
224
225 CodeGenerationConfig(std::optional<const std::string> ProteusOptPipeline,
226 CodegenOption ProteusCodegen, bool ProteusSpecializeArgs,
227 bool ProteusSpecializeLaunchBounds,
228 bool ProteusSpecializeDims,
229 bool ProteusSpecializeDimsRange, char ProteusOptLevel,
230 int ProteusCodeGenOptLevel, int TunedMaxThreads = -1,
231 int MinBlocksPerSM = 0,
232 std::optional<LaunchDim> TunedGridDim = std::nullopt,
233 std::optional<LaunchDim> TunedBlockDim = std::nullopt)
234 : ProteusOptPipeline(ProteusOptPipeline), ProteusCodegen(ProteusCodegen),
235 ProteusSpecializeArgs(ProteusSpecializeArgs),
236 ProteusSpecializeLaunchBounds(ProteusSpecializeLaunchBounds),
237 ProteusSpecializeDims(ProteusSpecializeDims),
238 ProteusSpecializeDimsRange(ProteusSpecializeDimsRange),
239 ProteusOptLevel(ProteusOptLevel),
240 ProteusCodeGenOptLevel(ProteusCodeGenOptLevel),
241 TunedMaxThreads(TunedMaxThreads), MinBlocksPerSM(MinBlocksPerSM),
242 TunedGridDim(TunedGridDim), TunedBlockDim(TunedBlockDim) {}
243
244public:
247 getEnvOrDefaultString("PROTEUS_OPT_PIPELINE"),
248 getCodeGen(getEnvOrDefaultCG("PROTEUS_CODEGEN", CodegenOption::RTC)),
249 getEnvOrDefaultBool("PROTEUS_SPECIALIZE_ARGS", true),
250 getEnvOrDefaultBool("PROTEUS_SPECIALIZE_LAUNCH_BOUNDS", true),
251 getEnvOrDefaultBool("PROTEUS_SPECIALIZE_DIMS", true),
252 // SpecializeDimsRange is disabled by default due to observed
253 // regressions for HIP and CUDA code generation
254 getEnvOrDefaultBool("PROTEUS_SPECIALIZE_DIMS_RANGE", false),
255 getEnvOrDefaultChar("PROTEUS_OPT_LEVEL", '3'),
256 getEnvOrDefaultInt("PROTEUS_CODEGEN_OPT_LEVEL", 3));
257 }
258
260 createFromJSONEntry(const llvm::json::Object &Config) {
261 auto Pipeline = Config.getString("Pipeline");
262 std::optional<std::string> ProteusPipeline;
263 if (Pipeline)
264 ProteusPipeline = Pipeline.value().str();
265
267 ProteusPipeline,
268 getCodeGen(
269 strToCG(getDefaultValueFromOptional(Config.getString("CodeGen"),
270 llvm::StringRef("rtc"))
271 .str())),
272 getDefaultValueFromOptional(Config.getBoolean("SpecializeArgs"), true),
273 getDefaultValueFromOptional(Config.getBoolean("LaunchBounds"), true),
274 getDefaultValueFromOptional(Config.getBoolean("SpecializeDims"), true),
275 getDefaultValueFromOptional(Config.getBoolean("SpecializeDimsRange"),
276 false),
277 getDefaultValueFromOptional(Config.getString("OptLevel"),
278 llvm::StringRef("3"))[0],
279 getDefaultValueFromOptional(Config.getInteger("CodeGenOptLevel"),
280 static_cast<int64_t>(3)),
281 getDefaultValueFromOptional(Config.getInteger("TunedMaxThreads"),
282 static_cast<int64_t>(-1L)),
283 getDefaultValueFromOptional(Config.getInteger("MinBlocksPerSM"),
284 static_cast<int64_t>(0L)),
285 getTunedDim(Config, "GridDim"), getTunedDim(Config, "BlockDim"));
286 }
287
288 CodegenOption codeGenOption() const { return ProteusCodegen; }
289 bool specializeArgs() const { return ProteusSpecializeArgs; }
290 bool specializeDims() const { return ProteusSpecializeDims; }
291 bool specializeDimsRange() const { return ProteusSpecializeDimsRange; }
292 bool specializeLaunchBounds() const { return ProteusSpecializeLaunchBounds; }
293 char optLevel() const { return ProteusOptLevel; }
294 int codeGenOptLevel() const { return ProteusCodeGenOptLevel; }
295 std::optional<const std::string> optPipeline() const {
296 return ProteusOptPipeline;
297 }
298 const std::optional<LaunchDim> &gridDim() const { return TunedGridDim; }
299 const std::optional<LaunchDim> &blockDim() const { return TunedBlockDim; }
300
301 int minBlocksPerSM(int MaxThreads) const {
302 // NOTE: We only return the tuned value when the current LBMaxThreads is
303 // equal to the tuned one. otherwise we return 0. Not doing so, may result
304 // in violating constraints in cases in which LBMaxThreads >
305 // TunedMaxThreads
306 if (TunedMaxThreads != MaxThreads)
307 return 0;
308 return MinBlocksPerSM;
309 }
310
311 template <typename T> void dump(T &OS) const {
312 if (ProteusOptPipeline)
313 OS << "Pipeline:" << ProteusOptPipeline.value() << " ";
314
315 OS << "CG:" << toString(ProteusCodegen) << " ";
316 OS << "SA:" << ProteusSpecializeArgs << " ";
317 OS << "LB:" << ProteusSpecializeLaunchBounds << " ";
318 OS << "SD:" << ProteusSpecializeDims << " ";
319 OS << "SDR:" << ProteusSpecializeDimsRange << " ";
320 OS << "OL:" << ProteusOptLevel << " ";
321 OS << "CGL:" << ProteusCodeGenOptLevel << " ";
322 OS << "TMT:" << TunedMaxThreads << " ";
323 OS << "BPSM:" << MinBlocksPerSM << " ";
324 if (TunedGridDim)
325 OS << "GridDim:(" << (*TunedGridDim)[0] << "," << (*TunedGridDim)[1]
326 << "," << (*TunedGridDim)[2] << ") ";
327 if (TunedBlockDim)
328 OS << "BlockDim:(" << (*TunedBlockDim)[0] << "," << (*TunedBlockDim)[1]
329 << "," << (*TunedBlockDim)[2] << ") ";
330 }
331};
332
333inline llvm::StringMap<const CodeGenerationConfig>
334parseJSONConfig(std::optional<std::string> JSONFn) {
335 llvm::StringMap<const CodeGenerationConfig> TunedConfigs;
336 if (!JSONFn)
337 return TunedConfigs;
338
339 auto JSONRoot = [&JSONFn]() -> std::optional<llvm::json::Object> {
340 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> JSONBuf =
341 llvm::MemoryBuffer::getFile(JSONFn.value(), /* isText */ true,
342 /* RequiresNullTerminator */ true);
343 if (!JSONBuf)
344 reportFatalError("Error when opening json file: " +
345 JSONBuf.getError().message() + "\n");
346
347 llvm::json::Value JsonInfo =
348 llvm::cantFail(llvm::json::parse(JSONBuf.get()->getBuffer()),
349 "Cannot convert buffer to json value");
350
351 if (auto *Obj = JsonInfo.getAsObject())
352 return *Obj;
353 return std::nullopt;
354 }();
355
356 if (!JSONRoot)
357 reportFatalError("Top-level JSON is not an object.\n");
358
359 for (auto &KV : JSONRoot.value()) {
360 auto KernelName = KV.first;
361 if (const auto *Options = KV.second.getAsObject()) {
362 TunedConfigs.try_emplace(
364 }
365 }
366 return TunedConfigs;
367}
368
369class Config {
370public:
371 static Config &get() {
372 static Config Conf;
373 return Conf;
374 }
376 const llvm::StringMap<const CodeGenerationConfig> TunedConfigs;
388 std::optional<const std::string> ProteusCacheDir;
389 std::optional<const std::string> ProteusClangxxBin;
390 std::optional<const std::string> ProteusNvccBin;
396
397 const CodeGenerationConfig &getCGConfig(llvm::StringRef KName = "") const {
398
399 if (TunedConfigs.empty())
400 return GlobalCodeGenConfig;
401
402 if (auto It = TunedConfigs.find(KName); It != TunedConfigs.end())
403 return It->second;
404
405 return GlobalCodeGenConfig;
406 }
407
408 bool traceSpecializations() const {
409 return ProteusTraceConfig &
410 static_cast<unsigned>(TraceOption::Specialization);
411 }
412 bool traceIRDump() const {
413 return ProteusTraceConfig & static_cast<unsigned>(TraceOption::IRDump);
414 }
415 bool traceKernels() const {
416 return ProteusTraceConfig & static_cast<unsigned>(TraceOption::KernelTrace);
417 }
418 bool traceCacheStats() const {
419 return ProteusTraceConfig & static_cast<unsigned>(TraceOption::CacheStats);
420 }
421
422 void dump(llvm::raw_ostream &OS) const {
423 auto PrintOut = [](llvm::StringRef ID,
424 const CodeGenerationConfig &KConfig) {
425 llvm::SmallString<128> S;
426 llvm::raw_svector_ostream OS(S);
427 OS << "ID:" << ID << " ";
428 KConfig.dump(OS);
429 return S;
430 };
431
432 OS << "PROTEUS_USE_STORED_CACHE " << ProteusUseStoredCache << "\n";
433 OS << "PROTEUS_CACHE_DIR " << Config::get().ProteusCacheDir << "\n";
434 OS << "PROTEUS_CLANGXX_BIN " << Config::get().ProteusClangxxBin << "\n";
435 OS << "PROTEUS_NVCC_BIN " << Config::get().ProteusNvccBin << "\n";
436
437 OS << PrintOut("Default", GlobalCodeGenConfig) << "\n";
438 for (auto &KV : TunedConfigs) {
439 OS << PrintOut(KV.getKey(), KV.second) << "\n";
440 }
441 }
442
443private:
444 Config()
445 : GlobalCodeGenConfig(CodeGenerationConfig::createFromEnv()),
447 parseJSONConfig(getEnvOrDefaultString("PROTEUS_TUNED_KERNELS"))),
448 ProteusCacheDir(getEnvOrDefaultString("PROTEUS_CACHE_DIR")),
449 ProteusClangxxBin(getEnvOrDefaultString("PROTEUS_CLANGXX_BIN")),
450 ProteusNvccBin(getEnvOrDefaultString("PROTEUS_NVCC_BIN")) {
452 getEnvOrDefaultBool("PROTEUS_USE_STORED_CACHE", true);
453 ProteusDisable = getEnvOrDefaultBool("PROTEUS_DISABLE", false);
454 ProteusDumpLLVMIR = getEnvOrDefaultBool("PROTEUS_DUMP_LLVM_IR", false);
456 getEnvOrDefaultBool("PROTEUS_RELINK_GLOBALS_BY_COPY", false);
458 getEnvOrDefaultBool("PROTEUS_ASYNC_COMPILATION", false);
460 getEnvOrDefaultBool("PROTEUS_ASYNC_TEST_BLOCKING", false);
461 ProteusAsyncThreads = getEnvOrDefaultInt("PROTEUS_ASYNC_THREADS", 1);
462 ProteusKernelClone = getEnvOrDefaultKC("PROTEUS_KERNEL_CLONE",
464 ProteusEnableTimers = getEnvOrDefaultBool("PROTEUS_ENABLE_TIMERS", false);
465 ProteusTraceConfig = parseTraceConfig("PROTEUS_TRACE_OUTPUT");
466 ProteusDebugOutput = getEnvOrDefaultBool("PROTEUS_DEBUG_OUTPUT", false);
468 getEnvOrDefaultString("PROTEUS_OBJECT_CACHE_CHAIN").value_or("storage");
470 getEnvOrDefaultBool("PROTEUS_ENABLE_TIME_TRACE", false);
472 getEnvOrDefaultString("PROTEUS_TIME_TRACE_FILE").value_or("");
474 getEnvOrDefaultInt("PROTEUS_TIME_TRACE_GRAIN", 500);
476 reportFatalError("PROTEUS_TIME_TRACE_GRAIN must be > 0");
478 getEnvOrDefaultInt("PROTEUS_COMM_THREAD_POLL_MS", 25);
479 }
480};
481} // namespace proteus
482
483#define PROTEUS_TIMER_OUTPUT(x) \
484 if (::proteus::Config::get().ProteusEnableTimers) \
485 x;
486
487#endif
const void const char * VarName
Definition CompilerInterfaceDevice.cpp:32
Definition Config.h:167
const std::optional< LaunchDim > & blockDim() const
Definition Config.h:299
void dump(T &OS) const
Definition Config.h:311
int minBlocksPerSM(int MaxThreads) const
Definition Config.h:301
bool specializeArgs() const
Definition Config.h:289
const std::optional< LaunchDim > & gridDim() const
Definition Config.h:298
bool specializeDimsRange() const
Definition Config.h:291
static CodeGenerationConfig createFromJSONEntry(const llvm::json::Object &Config)
Definition Config.h:260
bool specializeLaunchBounds() const
Definition Config.h:292
std::optional< const std::string > optPipeline() const
Definition Config.h:295
static CodeGenerationConfig createFromEnv()
Definition Config.h:245
bool specializeDims() const
Definition Config.h:290
char optLevel() const
Definition Config.h:293
CodegenOption codeGenOption() const
Definition Config.h:288
int codeGenOptLevel() const
Definition Config.h:294
Definition Config.h:369
int ProteusCommThreadPollMs
Definition Config.h:395
unsigned ProteusTraceConfig
Definition Config.h:386
bool ProteusDisable
Definition Config.h:378
static Config & get()
Definition Config.h:371
std::string ProteusObjectCacheChain
Definition Config.h:391
bool traceKernels() const
Definition Config.h:415
bool ProteusRelinkGlobalsByCopy
Definition Config.h:380
const llvm::StringMap< const CodeGenerationConfig > TunedConfigs
Definition Config.h:376
bool traceIRDump() const
Definition Config.h:412
std::optional< const std::string > ProteusClangxxBin
Definition Config.h:389
bool traceCacheStats() const
Definition Config.h:418
bool ProteusDumpLLVMIR
Definition Config.h:379
bool ProteusDebugOutput
Definition Config.h:387
const CodeGenerationConfig GlobalCodeGenConfig
Definition Config.h:375
bool ProteusUseStoredCache
Definition Config.h:377
int ProteusAsyncThreads
Definition Config.h:382
KernelCloneOption ProteusKernelClone
Definition Config.h:384
void dump(llvm::raw_ostream &OS) const
Definition Config.h:422
std::optional< const std::string > ProteusNvccBin
Definition Config.h:390
const CodeGenerationConfig & getCGConfig(llvm::StringRef KName="") const
Definition Config.h:397
int ProteusTimeTraceGrainUs
Definition Config.h:394
std::optional< const std::string > ProteusCacheDir
Definition Config.h:388
bool ProteusEnableTimers
Definition Config.h:385
bool traceSpecializations() const
Definition Config.h:408
bool ProteusAsyncTestBlocking
Definition Config.h:383
bool ProteusEnableTimeTrace
Definition Config.h:392
bool ProteusAsyncCompilation
Definition Config.h:381
std::string ProteusTimeTraceFile
Definition Config.h:393
Definition KernelName.h:18
static llvm::raw_ostream & outs(const std::string &Name)
Definition Logger.h:25
Definition MemoryCache.h:27
int getEnvOrDefaultInt(const char *VarName, int Default)
Definition Config.h:78
TraceOption
Definition Config.h:113
std::optional< std::string > getEnvOrDefaultString(const char *VarName)
Definition Config.h:57
void reportFatalError(const llvm::Twine &Reason, const char *FILE, unsigned Line)
Definition Error.cpp:14
CodegenOption
Definition Config.h:19
CodegenOption getEnvOrDefaultCG(const char *VarName, CodegenOption Default)
Definition Config.h:96
unsigned parseTraceConfig(const char *VarName)
Definition Config.h:120
KernelCloneOption
Definition Config.h:25
std::string toString(CodegenOption Option)
Definition Config.h:31
KernelCloneOption getEnvOrDefaultKC(const char *VarName, KernelCloneOption Default)
Definition Config.h:145
llvm::StringMap< const CodeGenerationConfig > parseJSONConfig(std::optional< std::string > JSONFn)
Definition Config.h:334
T getDefaultValueFromOptional(std::optional< T > JSONValue, T Default)
Definition Config.h:107
char getEnvOrDefaultChar(const char *VarName, char Default)
Definition Config.h:66
CodegenOption strToCG(std::string CGstr)
Definition Config.h:84
bool getEnvOrDefaultBool(const char *VarName, bool Default)
Definition Config.h:72