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>, 7> &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')
276  "Invalid value for ONEAPI_DEVICE_SELECTOR environment "
277  "variable: value should not be null.");
278 
279  DeviceTargets =
281  }
282  Initialized = true;
283  return DeviceTargets;
284  }
285 };
286 
287 template <> class SYCLConfig<SYCL_ENABLE_DEFAULT_CONTEXTS> {
289 
290 public:
291  static bool get() {
292  constexpr bool DefaultValue = true;
293 
294  const char *ValStr = getCachedValue();
295 
296  if (!ValStr)
297  return DefaultValue;
298 
299  return ValStr[0] == '1';
300  }
301 
302  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
303 
304  static void resetWithValue(const char *Val) {
305  (void)getCachedValue(/*ResetCache=*/true, Val);
306  }
307 
308  static const char *getName() { return BaseT::MConfigName; }
309 
310 private:
311  static const char *getCachedValue(bool ResetCache = false,
312  const char *Val = nullptr) {
313  static const char *ValStr = BaseT::getRawValue();
314  if (ResetCache) {
315  ValStr = (Val != nullptr) ? Val : BaseT::getRawValue();
316  }
317  return ValStr;
318  }
319 };
320 
321 template <> class SYCLConfig<SYCL_QUEUE_THREAD_POOL_SIZE> {
323 
324 public:
325  static int get() {
326  static int Value = [] {
327  const char *ValueStr = BaseT::getRawValue();
328 
329  int Result = 1;
330 
331  if (ValueStr)
332  try {
333  Result = std::stoi(ValueStr);
334  } catch (...) {
336  "Invalid value for SYCL_QUEUE_THREAD_POOL_SIZE "
337  "environment variable: value should be a number");
338  }
339 
340  if (Result < 1)
341  throw exception(
343  "Invalid value for SYCL_QUEUE_THREAD_POOL_SIZE environment "
344  "variable: value should be larger than zero");
345 
346  return Result;
347  }();
348 
349  return Value;
350  }
351 };
352 
353 template <> class SYCLConfig<SYCL_CACHE_PERSISTENT> {
355 
356 public:
357  static constexpr bool Default = false; // default is disabled
358 
359  static bool get() { return getCachedValue(); }
360 
361  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
362 
363  static const char *getName() { return BaseT::MConfigName; }
364 
365 private:
366  static bool parseValue() {
367  // Check if deprecated opt-out env var is used, then warn.
369  std::cerr
371  << " environment variable is deprecated "
372  << "and has no effect. By default, persistent device code caching is "
373  << (Default ? "enabled." : "disabled.") << " Use " << getName()
374  << "=1/0 to enable/disable.\n";
375  }
376 
377  const char *ValStr = BaseT::getRawValue();
378  if (!ValStr)
379  return Default;
380  if (strlen(ValStr) != 1 || (ValStr[0] != '0' && ValStr[0] != '1')) {
381  std::string Msg =
382  std::string{"Invalid value for bool configuration variable "} +
383  getName() + std::string{": "} + ValStr;
384  throw exception(make_error_code(errc::runtime), Msg);
385  }
386  return ValStr[0] == '1';
387  }
388 
389  static bool getCachedValue(bool ResetCache = false) {
390  static bool Val = parseValue();
391  if (ResetCache)
392  Val = parseValue();
393  return Val;
394  }
395 };
396 
397 template <> class SYCLConfig<SYCL_CACHE_DIR> {
399 
400 public:
401  static std::string get() { return getCachedValue(); }
402 
403  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
404 
405  static const char *getName() { return BaseT::MConfigName; }
406 
407 private:
408  // If environment variables are not available return an empty string to
409  // identify that cache is not available.
410  static std::string parseValue() {
411  const char *RootDir = BaseT::getRawValue();
412  if (RootDir)
413  return RootDir;
414 
415  constexpr char DeviceCodeCacheDir[] = "/libsycl_cache";
416 
417 #if defined(__SYCL_RT_OS_LINUX)
418  const char *CacheDir = std::getenv("XDG_CACHE_HOME");
419  const char *HomeDir = std::getenv("HOME");
420  if (!CacheDir && !HomeDir)
421  return {};
422  std::string Res{
423  std::string(CacheDir ? CacheDir : (std::string(HomeDir) + "/.cache")) +
424  DeviceCodeCacheDir};
425 #else
426  const char *AppDataDir = std::getenv("AppData");
427  if (!AppDataDir)
428  return {};
429  std::string Res{std::string(AppDataDir) + DeviceCodeCacheDir};
430 #endif
431  return Res;
432  }
433 
434  static std::string getCachedValue(bool ResetCache = false) {
435  static std::string Val = parseValue();
436  if (ResetCache)
437  Val = parseValue();
438  return Val;
439  }
440 };
441 
442 template <> class SYCLConfig<SYCL_REDUCTION_PREFERRED_WORKGROUP_SIZE> {
444 
445  struct ParsedValue {
446  size_t CPU = 0;
447  size_t GPU = 0;
448  size_t Accelerator = 0;
449  };
450 
451 public:
452  static size_t get(info::device_type DeviceType) {
453  ParsedValue Value = getCachedValue();
454  return getRefByDeviceType(Value, DeviceType);
455  }
456 
457  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
458 
459  static const char *getName() { return BaseT::MConfigName; }
460 
461 private:
462  static size_t &getRefByDeviceType(ParsedValue &Value,
463  info::device_type DeviceType) {
464  switch (DeviceType) {
466  return Value.CPU;
468  return Value.GPU;
470  return Value.Accelerator;
471  default:
472  // Expect to get here if user used wrong device type. Include wildcard
473  // in the message even though it's handled in the caller.
475  BaseT, "Device types must be \"cpu\", \"gpu\", \"acc\", or \"*\".");
476  }
477  }
478 
479  static ParsedValue parseValue() {
480  const char *ValueRaw = BaseT::getRawValue();
481  ParsedValue Result{};
482 
483  // Default to 0 to signify an unset value.
484  if (!ValueRaw)
485  return Result;
486 
487  std::string ValueStr{ValueRaw};
488  auto DeviceTypeMap = getSyclDeviceTypeMap<true /*Enable 'acc'*/>();
489 
490  // Iterate over all configurations.
491  size_t Start = 0, End = 0;
492  do {
493  End = ValueStr.find(',', Start);
494  if (End == std::string::npos)
495  End = ValueStr.size();
496 
497  // Get a substring of the current configuration pair.
498  std::string DeviceConfigStr = ValueStr.substr(Start, End - Start);
499 
500  // Find the delimiter in the configuration pair.
501  size_t ConfigDelimLoc = DeviceConfigStr.find(':');
502  if (ConfigDelimLoc == std::string::npos)
504  BaseT, "Device-value pair \"" + DeviceConfigStr +
505  "\" does not contain the ':' delimiter.");
506 
507  // Split configuration pair into its constituents.
508  std::string DeviceConfigTypeStr =
509  DeviceConfigStr.substr(0, ConfigDelimLoc);
510  std::string DeviceConfigValueStr = DeviceConfigStr.substr(
511  ConfigDelimLoc + 1, DeviceConfigStr.size() - ConfigDelimLoc - 1);
512 
513  // Find the device type in the "device type map".
514  auto DeviceTypeIter = std::find_if(
515  std::begin(DeviceTypeMap), std::end(DeviceTypeMap),
516  [&](auto Element) { return DeviceConfigTypeStr == Element.first; });
517  if (DeviceTypeIter == DeviceTypeMap.end())
519  BaseT,
520  "\"" + DeviceConfigTypeStr + "\" is not a recognized device type.");
521 
522  // Parse the configuration value.
523  int DeviceConfigValue = 1;
524  try {
525  DeviceConfigValue = std::stoi(DeviceConfigValueStr);
526  } catch (...) {
528  BaseT, "Value \"" + DeviceConfigValueStr + "\" must be a number");
529  }
530 
531  if (DeviceConfigValue < 1)
532  throw INVALID_CONFIG_EXCEPTION(BaseT,
533  "Value \"" + DeviceConfigValueStr +
534  "\" must be larger than zero");
535 
536  if (DeviceTypeIter->second == info::device_type::all) {
537  // Set all configuration values if we got the device-type wildcard.
538  Result.GPU = DeviceConfigValue;
539  Result.CPU = DeviceConfigValue;
540  Result.Accelerator = DeviceConfigValue;
541  } else {
542  // Try setting the corresponding configuration.
543  getRefByDeviceType(Result, DeviceTypeIter->second) = DeviceConfigValue;
544  }
545 
546  // Move to the start of the next configuration. If the start is outside
547  // the full value string we are done.
548  Start = End + 1;
549  } while (Start < ValueStr.size());
550  return Result;
551  }
552 
553  static ParsedValue getCachedValue(bool ResetCache = false) {
554  static ParsedValue Val = parseValue();
555  if (ResetCache)
556  Val = parseValue();
557  return Val;
558  }
559 };
560 
561 template <> class SYCLConfig<SYCL_ENABLE_FUSION_CACHING> {
563 
564 public:
565  static bool get() {
566  constexpr bool DefaultValue = true;
567 
568  const char *ValStr = getCachedValue();
569 
570  if (!ValStr)
571  return DefaultValue;
572 
573  return ValStr[0] == '1';
574  }
575 
576  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
577 
578  static const char *getName() { return BaseT::MConfigName; }
579 
580 private:
581  static const char *getCachedValue(bool ResetCache = false) {
582  static const char *ValStr = BaseT::getRawValue();
583  if (ResetCache)
584  ValStr = BaseT::getRawValue();
585  return ValStr;
586  }
587 };
588 
589 template <> class SYCLConfig<SYCL_CACHE_IN_MEM> {
591 
592 public:
593  static constexpr bool Default = true; // default is true
594  static bool get() { return getCachedValue(); }
595  static const char *getName() { return BaseT::MConfigName; }
596  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
597 
598 private:
599  static bool parseValue() {
600  const char *ValStr = BaseT::getRawValue();
601  if (!ValStr)
602  return Default;
603  if (strlen(ValStr) != 1 || (ValStr[0] != '0' && ValStr[0] != '1')) {
604  std::string Msg =
605  std::string{"Invalid value for bool configuration variable "} +
606  getName() + std::string{": "} + ValStr;
608  }
609  return ValStr[0] == '1';
610  }
611 
612  static bool getCachedValue(bool ResetCache = false) {
613  static bool Val = parseValue();
614  if (ResetCache) {
615  Val = BaseT::getRawValue();
616  }
617  return Val;
618  }
619 };
620 
621 template <> class SYCLConfig<SYCL_JIT_AMDGCN_PTX_KERNELS> {
623 
624 public:
625  static bool get() {
626  constexpr bool DefaultValue = false;
627  const char *ValStr = getCachedValue();
628  if (!ValStr)
629  return DefaultValue;
630 
631  return ValStr[0] == '1';
632  }
633 
634  static const char *getName() { return BaseT::MConfigName; }
635 
636 private:
637  static const char *getCachedValue(bool ResetCache = false) {
638  static const char *ValStr = BaseT::getRawValue();
639  if (ResetCache)
640  ValStr = BaseT::getRawValue();
641  return ValStr;
642  }
643 };
644 
645 template <> class SYCLConfig<SYCL_JIT_AMDGCN_PTX_TARGET_CPU> {
647 
648 public:
649  static std::string get() {
650  const std::string DefaultValue{""};
651 
652  const char *ValStr = getCachedValue();
653 
654  if (!ValStr)
655  return DefaultValue;
656 
657  return std::string{ValStr};
658  }
659 
660  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
661 
662  static const char *getName() { return BaseT::MConfigName; }
663 
664 private:
665  static const char *getCachedValue(bool ResetCache = false) {
666  static const char *ValStr = BaseT::getRawValue();
667  if (ResetCache)
668  ValStr = BaseT::getRawValue();
669  return ValStr;
670  }
671 };
672 
673 template <> class SYCLConfig<SYCL_JIT_AMDGCN_PTX_TARGET_FEATURES> {
675 
676 public:
677  static std::string get() {
678  const std::string DefaultValue{""};
679 
680  const char *ValStr = getCachedValue();
681 
682  if (!ValStr)
683  return DefaultValue;
684 
685  return std::string{ValStr};
686  }
687 
688  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
689 
690  static const char *getName() { return BaseT::MConfigName; }
691 
692 private:
693  static const char *getCachedValue(bool ResetCache = false) {
694  static const char *ValStr = BaseT::getRawValue();
695  if (ResetCache)
696  ValStr = BaseT::getRawValue();
697  return ValStr;
698  }
699 };
700 
701 #undef INVALID_CONFIG_EXCEPTION
702 
703 } // namespace detail
704 } // namespace _V1
705 } // 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
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
const std::array< std::pair< std::string, backend >, 7 > & getSyclBeMap()
Definition: config.cpp:168
constexpr bool ConfigFromCompileDefEnabled
Definition: config.hpp:45
constexpr int MAX_CONFIG_NAME
Definition: config.hpp:48
std::error_code make_error_code(sycl::errc E) noexcept
Constructs an error code using e and sycl_category()
Definition: exception.cpp:64
Definition: access.hpp:18
C++ wrapper of extern "C" PI interfaces.