DPC++ Runtime
Runtime libraries for oneAPI DPC++
config.hpp
Go to the documentation of this file.
1 //==---------------- config.hpp - SYCL config -------------------*- C++-*---==//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #pragma once
10 
12 #include <sycl/backend_types.hpp>
13 #include <sycl/detail/defines.hpp>
15 #include <sycl/detail/pi.hpp>
16 #include <sycl/exception.hpp>
17 #include <sycl/info/info_desc.hpp>
18 
19 #include <algorithm>
20 #include <array>
21 #include <cstdlib>
22 #include <mutex>
23 #include <string>
24 #include <utility>
25 
26 namespace sycl {
27 inline namespace _V1 {
28 namespace detail {
29 
30 #ifdef DISABLE_CONFIG_FROM_ENV
31 constexpr bool ConfigFromEnvEnabled = false;
32 #else
33 constexpr bool ConfigFromEnvEnabled = true;
34 #endif // DISABLE_CONFIG_FROM_ENV
35 
36 #ifdef DISABLE_CONFIG_FROM_CONFIG_FILE
37 constexpr bool ConfigFromFileEnabled = false;
38 #else
39 constexpr bool ConfigFromFileEnabled = true;
40 #endif // DISABLE_CONFIG_FROM_CONFIG_FILE
41 
42 #ifdef DISABLE_CONFIG_FROM_COMPILE_TIME
43 constexpr bool ConfigFromCompileDefEnabled = false;
44 #else
45 constexpr bool ConfigFromCompileDefEnabled = true;
46 #endif // DISABLE_CONFIG_FROM_COMPILE_TIME
47 
48 constexpr int MAX_CONFIG_NAME = 256;
49 constexpr int MAX_CONFIG_VALUE = 1024;
50 
51 // Enum of config IDs for accessing other arrays
52 enum ConfigID {
53  START = 0,
54 #define CONFIG(name, ...) name,
55 #include "config.def"
56 #undef CONFIG
57  END
58 };
59 
60 // Consider strings starting with __ as unset
61 constexpr const char *getStrOrNullptr(const char *Str) {
62  return (Str[0] == '_' && Str[1] == '_') ? nullptr : Str;
63 }
64 
65 // Intializes configs from the configuration file
66 void readConfig(bool ForceInitialization = false);
67 
68 template <ConfigID Config> class SYCLConfigBase;
69 
70 #define CONFIG(Name, MaxSize, CompileTimeDef) \
71  template <> class SYCLConfigBase<Name> { \
72  public: \
73  /*Preallocated storage for config value which is extracted from a config \
74  * file*/ \
75  static char MStorage[MaxSize + 1]; \
76  /*Points to the storage if config is set in the file, nullptr otherwise*/ \
77  static const char *MValueFromFile; \
78  /*The name of the config*/ \
79  static const char *const MConfigName; \
80  /*Points to the value which is set during compilation, nullptr otherwise. \
81  * Detection of whether a value is set or not is based on checking the \
82  * beginning of the string, if it starts with double underscore(__) the \
83  * value is not set.*/ \
84  static const char *const MCompileTimeDef; \
85  \
86  static const char *getRawValue() { \
87  if (ConfigFromEnvEnabled) \
88  if (const char *ValStr = getenv(MConfigName)) \
89  return ValStr; \
90  \
91  if (ConfigFromFileEnabled) { \
92  readConfig(); \
93  if (MValueFromFile) \
94  return MValueFromFile; \
95  } \
96  \
97  if (ConfigFromCompileDefEnabled && MCompileTimeDef) \
98  return MCompileTimeDef; \
99  \
100  return nullptr; \
101  } \
102  };
103 #include "config.def"
104 #undef CONFIG
105 
106 #define INVALID_CONFIG_EXCEPTION(BASE, MSG) \
107  sycl::exception(sycl::make_error_code(sycl::errc::invalid), \
108  "Invalid value for " + std::string{BASE::MConfigName} + \
109  " environment variable: " + MSG)
110 
111 template <ConfigID Config> class SYCLConfig {
113 
114 public:
115  static const char *get() { return getCachedValue(); }
116 
117  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
118 
119  static const char *getName() { return BaseT::MConfigName; }
120 
121 private:
122  static const char *getCachedValue(bool ResetCache = false) {
123  static const char *ValStr = BaseT::getRawValue();
124  if (ResetCache)
125  ValStr = BaseT::getRawValue();
126  return ValStr;
127  }
128 };
129 
130 template <> class SYCLConfig<SYCL_PI_TRACE> {
132 
133 public:
134  static int get() {
135  static bool Initialized = false;
136  // We don't use TraceLevel enum here because user can provide any bitmask
137  // which can correspond to several enum values.
138  static int Level = 0; // No tracing by default
139 
140  // Configuration parameters are processed only once, like reading a string
141  // from environment and converting it into a typed object.
142  if (Initialized)
143  return Level;
144 
145  const char *ValStr = BaseT::getRawValue();
146  Level = (ValStr ? std::atoi(ValStr) : 0);
147  Initialized = true;
148  return Level;
149  }
150 };
151 
152 template <> class SYCLConfig<SYCL_RT_WARNING_LEVEL> {
154 
155 public:
156  static unsigned int get() { return getCachedValue(); }
157 
158  static void reset() { (void)getCachedValue(true); }
159 
160 private:
161  static unsigned int getCachedValue(bool ResetCache = false) {
162  const auto Parser = []() {
163  const char *ValStr = BaseT::getRawValue();
164  int SignedLevel = ValStr ? std::atoi(ValStr) : 0;
165  return SignedLevel >= 0 ? SignedLevel : 0;
166  };
167 
168  static unsigned int Level = Parser();
169  if (ResetCache)
170  Level = Parser();
171 
172  return Level;
173  }
174 };
175 
176 template <> class SYCLConfig<SYCL_PARALLEL_FOR_RANGE_ROUNDING_TRACE> {
178 
179 public:
180  static bool get() {
181  static const char *ValStr = BaseT::getRawValue();
182  return ValStr != nullptr;
183  }
184 };
185 
186 template <> class SYCLConfig<SYCL_DISABLE_PARALLEL_FOR_RANGE_ROUNDING> {
188 
189 public:
190  static bool get() {
191  static const char *ValStr = BaseT::getRawValue();
192  return ValStr != nullptr;
193  }
194 };
195 
196 template <> class SYCLConfig<SYCL_PARALLEL_FOR_RANGE_ROUNDING_PARAMS> {
198 
199 private:
200 public:
201  static void GetSettings(size_t &MinFactor, size_t &GoodFactor,
202  size_t &MinRange) {
203  static const char *RoundParams = BaseT::getRawValue();
204  if (RoundParams == nullptr)
205  return;
206 
207  static bool ProcessedFactors = false;
208  static size_t MF;
209  static size_t GF;
210  static size_t MR;
211  if (!ProcessedFactors) {
212  // Parse optional parameters of this form (all values required):
213  // MinRound:PreferredRound:MinRange
214  std::string Params(RoundParams);
215  size_t Pos = Params.find(':');
216  if (Pos != std::string::npos) {
217  MF = std::stoi(Params.substr(0, Pos));
218  Params.erase(0, Pos + 1);
219  Pos = Params.find(':');
220  if (Pos != std::string::npos) {
221  GF = std::stoi(Params.substr(0, Pos));
222  Params.erase(0, Pos + 1);
223  MR = std::stoi(Params);
224  }
225  }
226  ProcessedFactors = true;
227  }
228  MinFactor = MF;
229  GoodFactor = GF;
230  MinRange = MR;
231  }
232 };
233 
234 // Array is used by SYCL_DEVICE_ALLOWLIST and ONEAPI_DEVICE_SELECTOR.
235 // The 'supportAcc' parameter is used by SYCL_DEVICE_ALLOWLIST which,
236 // unlike ONEAPI_DEVICE_SELECTOR, also accepts 'acc' as a valid device type.
237 template <bool supportAcc = false>
238 const std::array<std::pair<std::string, info::device_type>, 6> &
240  static const std::array<std::pair<std::string, info::device_type>, 6>
241  SyclDeviceTypeMap = {
242  {{"host", info::device_type::host},
243  {"cpu", info::device_type::cpu},
244  {"gpu", info::device_type::gpu},
245  /* Duplicate entries are fine as this map is one-directional.*/
246  {supportAcc ? "acc" : "fpga", info::device_type::accelerator},
248  {"*", info::device_type::all}}};
249  return SyclDeviceTypeMap;
250 }
251 
252 // Array is used by SYCL_DEVICE_FILTER and SYCL_DEVICE_ALLOWLIST and
253 // ONEAPI_DEVICE_SELECTOR
254 const std::array<std::pair<std::string, backend>, 8> &getSyclBeMap();
255 
256 // ---------------------------------------
257 // ONEAPI_DEVICE_SELECTOR support
258 template <> class SYCLConfig<ONEAPI_DEVICE_SELECTOR> {
260 
261 public:
262  static ods_target_list *get() {
263  // Configuration parameters are processed only once, like reading a string
264  // from environment and converting it into a typed object.
265  static bool Initialized = false;
266  static ods_target_list *DeviceTargets = nullptr;
267 
268  if (Initialized) {
269  return DeviceTargets;
270  }
271  const char *ValStr = BaseT::getRawValue();
272  if (ValStr) {
273  // Throw if the input string is empty.
274  if (ValStr[0] == '\0')
275  throw invalid_parameter_error(
276  "Invalid value for ONEAPI_DEVICE_SELECTOR environment "
277  "variable: value should not be null.",
278  PI_ERROR_INVALID_VALUE);
279 
280  DeviceTargets =
282  }
283  Initialized = true;
284  return DeviceTargets;
285  }
286 };
287 
288 template <> class SYCLConfig<SYCL_ENABLE_DEFAULT_CONTEXTS> {
290 
291 public:
292  static bool get() {
293  constexpr bool DefaultValue = true;
294 
295  const char *ValStr = getCachedValue();
296 
297  if (!ValStr)
298  return DefaultValue;
299 
300  return ValStr[0] == '1';
301  }
302 
303  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
304 
305  static void resetWithValue(const char *Val) {
306  (void)getCachedValue(/*ResetCache=*/true, Val);
307  }
308 
309  static const char *getName() { return BaseT::MConfigName; }
310 
311 private:
312  static const char *getCachedValue(bool ResetCache = false,
313  const char *Val = nullptr) {
314  static const char *ValStr = BaseT::getRawValue();
315  if (ResetCache) {
316  ValStr = (Val != nullptr) ? Val : BaseT::getRawValue();
317  }
318  return ValStr;
319  }
320 };
321 
322 template <> class SYCLConfig<SYCL_QUEUE_THREAD_POOL_SIZE> {
324 
325 public:
326  static int get() {
327  static int Value = [] {
328  const char *ValueStr = BaseT::getRawValue();
329 
330  int Result = 1;
331 
332  if (ValueStr)
333  try {
334  Result = std::stoi(ValueStr);
335  } catch (...) {
336  throw invalid_parameter_error(
337  "Invalid value for SYCL_QUEUE_THREAD_POOL_SIZE environment "
338  "variable: value should be a number",
339  PI_ERROR_INVALID_VALUE);
340  }
341 
342  if (Result < 1)
343  throw invalid_parameter_error(
344  "Invalid value for SYCL_QUEUE_THREAD_POOL_SIZE environment "
345  "variable: value should be larger than zero",
346  PI_ERROR_INVALID_VALUE);
347 
348  return Result;
349  }();
350 
351  return Value;
352  }
353 };
354 
355 template <> class SYCLConfig<SYCL_CACHE_PERSISTENT> {
357 
358 public:
359  static constexpr bool Default = false; // default is disabled
360 
361  static bool get() { return getCachedValue(); }
362 
363  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
364 
365  static const char *getName() { return BaseT::MConfigName; }
366 
367 private:
368  static bool parseValue() {
369  // Check if deprecated opt-out env var is used, then warn.
371  std::cerr
373  << " environment variable is deprecated "
374  << "and has no effect. By default, persistent device code caching is "
375  << (Default ? "enabled." : "disabled.") << " Use " << getName()
376  << "=1/0 to enable/disable.\n";
377  }
378 
379  const char *ValStr = BaseT::getRawValue();
380  if (!ValStr)
381  return Default;
382  if (strlen(ValStr) != 1 || (ValStr[0] != '0' && ValStr[0] != '1')) {
383  std::string Msg =
384  std::string{"Invalid value for bool configuration variable "} +
385  getName() + std::string{": "} + ValStr;
386  throw runtime_error(Msg, PI_ERROR_INVALID_OPERATION);
387  }
388  return ValStr[0] == '1';
389  }
390 
391  static bool getCachedValue(bool ResetCache = false) {
392  static bool Val = parseValue();
393  if (ResetCache)
394  Val = parseValue();
395  return Val;
396  }
397 };
398 
399 template <> class SYCLConfig<SYCL_CACHE_DIR> {
401 
402 public:
403  static std::string get() { return getCachedValue(); }
404 
405  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
406 
407  static const char *getName() { return BaseT::MConfigName; }
408 
409 private:
410  // If environment variables are not available return an empty string to
411  // identify that cache is not available.
412  static std::string parseValue() {
413  const char *RootDir = BaseT::getRawValue();
414  if (RootDir)
415  return RootDir;
416 
417  constexpr char DeviceCodeCacheDir[] = "/libsycl_cache";
418 
419 #if defined(__SYCL_RT_OS_LINUX)
420  const char *CacheDir = std::getenv("XDG_CACHE_HOME");
421  const char *HomeDir = std::getenv("HOME");
422  if (!CacheDir && !HomeDir)
423  return {};
424  std::string Res{
425  std::string(CacheDir ? CacheDir : (std::string(HomeDir) + "/.cache")) +
426  DeviceCodeCacheDir};
427 #else
428  const char *AppDataDir = std::getenv("AppData");
429  if (!AppDataDir)
430  return {};
431  std::string Res{std::string(AppDataDir) + DeviceCodeCacheDir};
432 #endif
433  return Res;
434  }
435 
436  static std::string getCachedValue(bool ResetCache = false) {
437  static std::string Val = parseValue();
438  if (ResetCache)
439  Val = parseValue();
440  return Val;
441  }
442 };
443 
444 template <> class SYCLConfig<SYCL_REDUCTION_PREFERRED_WORKGROUP_SIZE> {
446 
447  struct ParsedValue {
448  size_t CPU = 0;
449  size_t GPU = 0;
450  size_t Accelerator = 0;
451  };
452 
453 public:
454  static size_t get(info::device_type DeviceType) {
455  ParsedValue Value = getCachedValue();
456  return getRefByDeviceType(Value, DeviceType);
457  }
458 
459  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
460 
461  static const char *getName() { return BaseT::MConfigName; }
462 
463 private:
464  static size_t &getRefByDeviceType(ParsedValue &Value,
465  info::device_type DeviceType) {
466  switch (DeviceType) {
468  return Value.CPU;
470  return Value.GPU;
472  return Value.Accelerator;
473  default:
474  // Expect to get here if user used wrong device type. Include wildcard
475  // in the message even though it's handled in the caller.
477  BaseT, "Device types must be \"cpu\", \"gpu\", \"acc\", or \"*\".");
478  }
479  }
480 
481  static ParsedValue parseValue() {
482  const char *ValueRaw = BaseT::getRawValue();
483  ParsedValue Result{};
484 
485  // Default to 0 to signify an unset value.
486  if (!ValueRaw)
487  return Result;
488 
489  std::string ValueStr{ValueRaw};
490  auto DeviceTypeMap = getSyclDeviceTypeMap<true /*Enable 'acc'*/>();
491 
492  // Iterate over all configurations.
493  size_t Start = 0, End = 0;
494  do {
495  End = ValueStr.find(',', Start);
496  if (End == std::string::npos)
497  End = ValueStr.size();
498 
499  // Get a substring of the current configuration pair.
500  std::string DeviceConfigStr = ValueStr.substr(Start, End - Start);
501 
502  // Find the delimiter in the configuration pair.
503  size_t ConfigDelimLoc = DeviceConfigStr.find(':');
504  if (ConfigDelimLoc == std::string::npos)
506  BaseT, "Device-value pair \"" + DeviceConfigStr +
507  "\" does not contain the ':' delimiter.");
508 
509  // Split configuration pair into its constituents.
510  std::string DeviceConfigTypeStr =
511  DeviceConfigStr.substr(0, ConfigDelimLoc);
512  std::string DeviceConfigValueStr = DeviceConfigStr.substr(
513  ConfigDelimLoc + 1, DeviceConfigStr.size() - ConfigDelimLoc - 1);
514 
515  // Find the device type in the "device type map".
516  auto DeviceTypeIter = std::find_if(
517  std::begin(DeviceTypeMap), std::end(DeviceTypeMap),
518  [&](auto Element) { return DeviceConfigTypeStr == Element.first; });
519  if (DeviceTypeIter == DeviceTypeMap.end())
521  BaseT,
522  "\"" + DeviceConfigTypeStr + "\" is not a recognized device type.");
523 
524  // Parse the configuration value.
525  int DeviceConfigValue = 1;
526  try {
527  DeviceConfigValue = std::stoi(DeviceConfigValueStr);
528  } catch (...) {
530  BaseT, "Value \"" + DeviceConfigValueStr + "\" must be a number");
531  }
532 
533  if (DeviceConfigValue < 1)
534  throw INVALID_CONFIG_EXCEPTION(BaseT,
535  "Value \"" + DeviceConfigValueStr +
536  "\" must be larger than zero");
537 
538  if (DeviceTypeIter->second == info::device_type::all) {
539  // Set all configuration values if we got the device-type wildcard.
540  Result.GPU = DeviceConfigValue;
541  Result.CPU = DeviceConfigValue;
542  Result.Accelerator = DeviceConfigValue;
543  } else {
544  // Try setting the corresponding configuration.
545  getRefByDeviceType(Result, DeviceTypeIter->second) = DeviceConfigValue;
546  }
547 
548  // Move to the start of the next configuration. If the start is outside
549  // the full value string we are done.
550  Start = End + 1;
551  } while (Start < ValueStr.size());
552  return Result;
553  }
554 
555  static ParsedValue getCachedValue(bool ResetCache = false) {
556  static ParsedValue Val = parseValue();
557  if (ResetCache)
558  Val = parseValue();
559  return Val;
560  }
561 };
562 
563 template <> class SYCLConfig<SYCL_ENABLE_FUSION_CACHING> {
565 
566 public:
567  static bool get() {
568  constexpr bool DefaultValue = true;
569 
570  const char *ValStr = getCachedValue();
571 
572  if (!ValStr)
573  return DefaultValue;
574 
575  return ValStr[0] == '1';
576  }
577 
578  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
579 
580  static const char *getName() { return BaseT::MConfigName; }
581 
582 private:
583  static const char *getCachedValue(bool ResetCache = false) {
584  static const char *ValStr = BaseT::getRawValue();
585  if (ResetCache)
586  ValStr = BaseT::getRawValue();
587  return ValStr;
588  }
589 };
590 
591 template <> class SYCLConfig<SYCL_CACHE_IN_MEM> {
593 
594 public:
595  static constexpr bool Default = true; // default is true
596  static bool get() { return getCachedValue(); }
597  static const char *getName() { return BaseT::MConfigName; }
598 
599 private:
600  static bool parseValue() {
601  const char *ValStr = BaseT::getRawValue();
602  if (!ValStr)
603  return Default;
604  if (strlen(ValStr) != 1 || (ValStr[0] != '0' && ValStr[0] != '1')) {
605  std::string Msg =
606  std::string{"Invalid value for bool configuration variable "} +
607  getName() + std::string{": "} + ValStr;
608  throw runtime_error(Msg, PI_ERROR_INVALID_OPERATION);
609  }
610  return ValStr[0] == '1';
611  }
612 
613  static bool getCachedValue() {
614  static bool Val = parseValue();
615  return Val;
616  }
617 };
618 
619 #undef INVALID_CONFIG_EXCEPTION
620 
621 } // namespace detail
622 } // namespace _V1
623 } // namespace sycl
ods_target_list & getOneapiDeviceSelectorTargets(const std::string &InitValue)
static GlobalHandler & instance()
static void GetSettings(size_t &MinFactor, size_t &GoodFactor, size_t &MinRange)
Definition: config.hpp:201
static const char * getName()
Definition: config.hpp:119
static const char * get()
Definition: config.hpp:115
#define INVALID_CONFIG_EXCEPTION(BASE, MSG)
Definition: config.hpp:106
__SYCL_EXTERN_STREAM_ATTRS ostream cerr
Linked to standard error (unbuffered)
constexpr bool ConfigFromEnvEnabled
Definition: config.hpp:33
void readConfig(bool ForceInitialization)
Definition: config.cpp:53
const std::array< std::pair< std::string, backend >, 8 > & getSyclBeMap()
Definition: config.cpp:169
constexpr int MAX_CONFIG_VALUE
Definition: config.hpp:49
const std::array< std::pair< std::string, info::device_type >, 6 > & getSyclDeviceTypeMap()
Definition: config.hpp:239
constexpr const char * getStrOrNullptr(const char *Str)
Definition: config.hpp:61
constexpr bool ConfigFromFileEnabled
Definition: config.hpp:39
constexpr bool ConfigFromCompileDefEnabled
Definition: config.hpp:45
constexpr int MAX_CONFIG_NAME
Definition: config.hpp:48
Definition: access.hpp:18
C++ wrapper of extern "C" PI interfaces.