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 #ifndef __INTEL_PREVIEW_BREAKING_CHANGES
289 // ---------------------------------------
290 // SYCL_DEVICE_FILTER support
291 
292 template <>
293 class __SYCL2020_DEPRECATED("Use SYCLConfig<ONEAPI_DEVICE_SELECTOR> instead")
294  SYCLConfig<SYCL_DEVICE_FILTER> {
295  using BaseT = SYCLConfigBase<SYCL_DEVICE_FILTER>;
296 
297 public:
298  static device_filter_list *get() {
299  static bool Initialized = false;
300  static device_filter_list *FilterList = nullptr;
301 
302  // Configuration parameters are processed only once, like reading a string
303  // from environment and converting it into a typed object.
304  if (Initialized) {
305  return FilterList;
306  }
307 
308  const char *ValStr = BaseT::getRawValue();
309  if (ValStr) {
310 
311  std::cerr
312  << "\nWARNING: The enviroment variable SYCL_DEVICE_FILTER"
313  " is deprecated. Please use ONEAPI_DEVICE_SELECTOR instead.\n"
314  "For more details, please refer to:\n"
315  "https://github.com/intel/llvm/blob/sycl/sycl/doc/"
316  "EnvironmentVariables.md#oneapi_device_selector\n\n";
317 
318  FilterList = &GlobalHandler::instance().getDeviceFilterList(ValStr);
319  }
320 
321  // As mentioned above, configuration parameters are processed only once.
322  // If multiple threads are checking this env var at the same time,
323  // they will end up setting the configration to the same value.
324  // If other threads check after one thread already set configration,
325  // the threads will get the same value as the first thread.
326  Initialized = true;
327  return FilterList;
328  }
329 };
330 #endif // __INTEL_PREVIEW_BREAKING_CHANGES
331 
332 template <> class SYCLConfig<SYCL_ENABLE_DEFAULT_CONTEXTS> {
334 
335 public:
336  static bool get() {
337  constexpr bool DefaultValue = true;
338 
339  const char *ValStr = getCachedValue();
340 
341  if (!ValStr)
342  return DefaultValue;
343 
344  return ValStr[0] == '1';
345  }
346 
347  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
348 
349  static void resetWithValue(const char *Val) {
350  (void)getCachedValue(/*ResetCache=*/true, Val);
351  }
352 
353  static const char *getName() { return BaseT::MConfigName; }
354 
355 private:
356  static const char *getCachedValue(bool ResetCache = false,
357  const char *Val = nullptr) {
358  static const char *ValStr = BaseT::getRawValue();
359  if (ResetCache) {
360  ValStr = (Val != nullptr) ? Val : BaseT::getRawValue();
361  }
362  return ValStr;
363  }
364 };
365 
366 template <> class SYCLConfig<SYCL_QUEUE_THREAD_POOL_SIZE> {
368 
369 public:
370  static int get() {
371  static int Value = [] {
372  const char *ValueStr = BaseT::getRawValue();
373 
374  int Result = 1;
375 
376  if (ValueStr)
377  try {
378  Result = std::stoi(ValueStr);
379  } catch (...) {
380  throw invalid_parameter_error(
381  "Invalid value for SYCL_QUEUE_THREAD_POOL_SIZE environment "
382  "variable: value should be a number",
383  PI_ERROR_INVALID_VALUE);
384  }
385 
386  if (Result < 1)
387  throw invalid_parameter_error(
388  "Invalid value for SYCL_QUEUE_THREAD_POOL_SIZE environment "
389  "variable: value should be larger than zero",
390  PI_ERROR_INVALID_VALUE);
391 
392  return Result;
393  }();
394 
395  return Value;
396  }
397 };
398 
399 template <> class SYCLConfig<SYCL_CACHE_PERSISTENT> {
401 
402 public:
403  static constexpr bool Default = false; // default is disabled
404 
405  static bool get() { return getCachedValue(); }
406 
407  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
408 
409  static const char *getName() { return BaseT::MConfigName; }
410 
411 private:
412  static bool parseValue() {
413  // Check if deprecated opt-out env var is used, then warn.
415  std::cerr
417  << " environment variable is deprecated "
418  << "and has no effect. By default, persistent device code caching is "
419  << (Default ? "enabled." : "disabled.") << " Use " << getName()
420  << "=1/0 to enable/disable.\n";
421  }
422 
423  const char *ValStr = BaseT::getRawValue();
424  if (!ValStr)
425  return Default;
426  if (strlen(ValStr) != 1 || (ValStr[0] != '0' && ValStr[0] != '1')) {
427  std::string Msg =
428  std::string{"Invalid value for bool configuration variable "} +
429  getName() + std::string{": "} + ValStr;
430  throw runtime_error(Msg, PI_ERROR_INVALID_OPERATION);
431  }
432  return ValStr[0] == '1';
433  }
434 
435  static bool getCachedValue(bool ResetCache = false) {
436  static bool Val = parseValue();
437  if (ResetCache)
438  Val = parseValue();
439  return Val;
440  }
441 };
442 
443 template <> class SYCLConfig<SYCL_CACHE_DIR> {
445 
446 public:
447  static std::string get() { return getCachedValue(); }
448 
449  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
450 
451  static const char *getName() { return BaseT::MConfigName; }
452 
453 private:
454  // If environment variables are not available return an empty string to
455  // identify that cache is not available.
456  static std::string parseValue() {
457  const char *RootDir = BaseT::getRawValue();
458  if (RootDir)
459  return RootDir;
460 
461  constexpr char DeviceCodeCacheDir[] = "/libsycl_cache";
462 
463 #if defined(__SYCL_RT_OS_LINUX)
464  const char *CacheDir = std::getenv("XDG_CACHE_HOME");
465  const char *HomeDir = std::getenv("HOME");
466  if (!CacheDir && !HomeDir)
467  return {};
468  std::string Res{
469  std::string(CacheDir ? CacheDir : (std::string(HomeDir) + "/.cache")) +
470  DeviceCodeCacheDir};
471 #else
472  const char *AppDataDir = std::getenv("AppData");
473  if (!AppDataDir)
474  return {};
475  std::string Res{std::string(AppDataDir) + DeviceCodeCacheDir};
476 #endif
477  return Res;
478  }
479 
480  static std::string getCachedValue(bool ResetCache = false) {
481  static std::string Val = parseValue();
482  if (ResetCache)
483  Val = parseValue();
484  return Val;
485  }
486 };
487 
488 template <> class SYCLConfig<SYCL_REDUCTION_PREFERRED_WORKGROUP_SIZE> {
490 
491  struct ParsedValue {
492  size_t CPU = 0;
493  size_t GPU = 0;
494  size_t Accelerator = 0;
495  };
496 
497 public:
498  static size_t get(info::device_type DeviceType) {
499  ParsedValue Value = getCachedValue();
500  return getRefByDeviceType(Value, DeviceType);
501  }
502 
503  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
504 
505  static const char *getName() { return BaseT::MConfigName; }
506 
507 private:
508  static size_t &getRefByDeviceType(ParsedValue &Value,
509  info::device_type DeviceType) {
510  switch (DeviceType) {
512  return Value.CPU;
514  return Value.GPU;
516  return Value.Accelerator;
517  default:
518  // Expect to get here if user used wrong device type. Include wildcard
519  // in the message even though it's handled in the caller.
521  BaseT, "Device types must be \"cpu\", \"gpu\", \"acc\", or \"*\".");
522  }
523  }
524 
525  static ParsedValue parseValue() {
526  const char *ValueRaw = BaseT::getRawValue();
527  ParsedValue Result{};
528 
529  // Default to 0 to signify an unset value.
530  if (!ValueRaw)
531  return Result;
532 
533  std::string ValueStr{ValueRaw};
534  auto DeviceTypeMap = getSyclDeviceTypeMap<true /*Enable 'acc'*/>();
535 
536  // Iterate over all configurations.
537  size_t Start = 0, End = 0;
538  do {
539  End = ValueStr.find(',', Start);
540  if (End == std::string::npos)
541  End = ValueStr.size();
542 
543  // Get a substring of the current configuration pair.
544  std::string DeviceConfigStr = ValueStr.substr(Start, End - Start);
545 
546  // Find the delimiter in the configuration pair.
547  size_t ConfigDelimLoc = DeviceConfigStr.find(':');
548  if (ConfigDelimLoc == std::string::npos)
550  BaseT, "Device-value pair \"" + DeviceConfigStr +
551  "\" does not contain the ':' delimiter.");
552 
553  // Split configuration pair into its constituents.
554  std::string DeviceConfigTypeStr =
555  DeviceConfigStr.substr(0, ConfigDelimLoc);
556  std::string DeviceConfigValueStr = DeviceConfigStr.substr(
557  ConfigDelimLoc + 1, DeviceConfigStr.size() - ConfigDelimLoc - 1);
558 
559  // Find the device type in the "device type map".
560  auto DeviceTypeIter = std::find_if(
561  std::begin(DeviceTypeMap), std::end(DeviceTypeMap),
562  [&](auto Element) { return DeviceConfigTypeStr == Element.first; });
563  if (DeviceTypeIter == DeviceTypeMap.end())
565  BaseT,
566  "\"" + DeviceConfigTypeStr + "\" is not a recognized device type.");
567 
568  // Parse the configuration value.
569  int DeviceConfigValue = 1;
570  try {
571  DeviceConfigValue = std::stoi(DeviceConfigValueStr);
572  } catch (...) {
574  BaseT, "Value \"" + DeviceConfigValueStr + "\" must be a number");
575  }
576 
577  if (DeviceConfigValue < 1)
578  throw INVALID_CONFIG_EXCEPTION(BaseT,
579  "Value \"" + DeviceConfigValueStr +
580  "\" must be larger than zero");
581 
582  if (DeviceTypeIter->second == info::device_type::all) {
583  // Set all configuration values if we got the device-type wildcard.
584  Result.GPU = DeviceConfigValue;
585  Result.CPU = DeviceConfigValue;
586  Result.Accelerator = DeviceConfigValue;
587  } else {
588  // Try setting the corresponding configuration.
589  getRefByDeviceType(Result, DeviceTypeIter->second) = DeviceConfigValue;
590  }
591 
592  // Move to the start of the next configuration. If the start is outside
593  // the full value string we are done.
594  Start = End + 1;
595  } while (Start < ValueStr.size());
596  return Result;
597  }
598 
599  static ParsedValue getCachedValue(bool ResetCache = false) {
600  static ParsedValue Val = parseValue();
601  if (ResetCache)
602  Val = parseValue();
603  return Val;
604  }
605 };
606 
607 template <> class SYCLConfig<SYCL_ENABLE_FUSION_CACHING> {
609 
610 public:
611  static bool get() {
612  constexpr bool DefaultValue = true;
613 
614  const char *ValStr = getCachedValue();
615 
616  if (!ValStr)
617  return DefaultValue;
618 
619  return ValStr[0] == '1';
620  }
621 
622  static void reset() { (void)getCachedValue(/*ResetCache=*/true); }
623 
624  static const char *getName() { return BaseT::MConfigName; }
625 
626 private:
627  static const char *getCachedValue(bool ResetCache = false) {
628  static const char *ValStr = BaseT::getRawValue();
629  if (ResetCache)
630  ValStr = BaseT::getRawValue();
631  return ValStr;
632  }
633 };
634 
635 template <> class SYCLConfig<SYCL_CACHE_IN_MEM> {
637 
638 public:
639  static constexpr bool Default = true; // default is true
640  static bool get() { return getCachedValue(); }
641  static const char *getName() { return BaseT::MConfigName; }
642 
643 private:
644  static bool parseValue() {
645  const char *ValStr = BaseT::getRawValue();
646  if (!ValStr)
647  return Default;
648  if (strlen(ValStr) != 1 || (ValStr[0] != '0' && ValStr[0] != '1')) {
649  std::string Msg =
650  std::string{"Invalid value for bool configuration variable "} +
651  getName() + std::string{": "} + ValStr;
652  throw runtime_error(Msg, PI_ERROR_INVALID_OPERATION);
653  }
654  return ValStr[0] == '1';
655  }
656 
657  static bool getCachedValue() {
658  static bool Val = parseValue();
659  return Val;
660  }
661 };
662 
663 #undef INVALID_CONFIG_EXCEPTION
664 
665 } // namespace detail
666 } // namespace _V1
667 } // namespace sycl
ods_target_list & getOneapiDeviceSelectorTargets(const std::string &InitValue)
device_filter_list & getDeviceFilterList(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
std::string string
Definition: handler.hpp:423
constexpr int MAX_CONFIG_NAME
Definition: config.hpp:48
std::uint8_t instead
Definition: aliases.hpp:93
signed char __SYCL2020_DEPRECATED
Definition: aliases.hpp:94
pointer get() const
Definition: multi_ptr.hpp:544
Definition: access.hpp:18
C++ wrapper of extern "C" PI interfaces.