defaults.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2022-2024, NVIDIA CORPORATION.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
21 #pragma once
22 
23 #include <algorithm>
24 #include <cstddef>
25 #include <cstdlib>
26 #include <sstream>
27 #include <stdexcept>
28 #include <string>
29 
30 #include <BS_thread_pool.hpp>
31 
32 #include <kvikio/shim/cufile.hpp>
33 
34 namespace kvikio {
38 enum class CompatMode : uint8_t {
39  OFF,
42  ON,
43  AUTO,
45 };
46 
47 namespace detail {
58 inline CompatMode parse_compat_mode_str(std::string_view compat_mode_str)
59 {
60  // Convert to lowercase
61  std::string tmp{compat_mode_str};
62  std::transform(
63  tmp.begin(), tmp.end(), tmp.begin(), [](unsigned char c) { return std::tolower(c); });
64 
65  CompatMode res{};
66  if (tmp == "on" || tmp == "true" || tmp == "yes" || tmp == "1") {
67  res = CompatMode::ON;
68  } else if (tmp == "off" || tmp == "false" || tmp == "no" || tmp == "0") {
69  res = CompatMode::OFF;
70  } else if (tmp == "auto") {
71  res = CompatMode::AUTO;
72  } else {
73  throw std::invalid_argument("Unknown compatibility mode: " + std::string{tmp});
74  }
75  return res;
76 }
77 
78 template <typename T>
79 T getenv_or(std::string_view env_var_name, T default_val)
80 {
81  const auto* env_val = std::getenv(env_var_name.data());
82  if (env_val == nullptr) { return default_val; }
83 
84  std::stringstream sstream(env_val);
85  T converted_val;
86  sstream >> converted_val;
87  if (sstream.fail()) {
88  throw std::invalid_argument("unknown config value " + std::string{env_var_name} + "=" +
89  std::string{env_val});
90  }
91  return converted_val;
92 }
93 
94 template <>
95 inline bool getenv_or(std::string_view env_var_name, bool default_val)
96 {
97  const auto* env_val = std::getenv(env_var_name.data());
98  if (env_val == nullptr) { return default_val; }
99  try {
100  // Try parsing `env_var_name` as a integer
101  return static_cast<bool>(std::stoi(env_val));
102  } catch (const std::invalid_argument&) {
103  }
104  // Convert to lowercase
105  std::string str{env_val};
106  // Special considerations regarding the case conversion:
107  // - std::tolower() is not an addressable function. Passing it to std::transform() as
108  // a function pointer, if the compile turns out successful, causes the program behavior
109  // "unspecified (possibly ill-formed)", hence the lambda. ::tolower() is addressable
110  // and does not have this problem, but the following item still applies.
111  // - To avoid UB in std::tolower() or ::tolower(), the character must be cast to unsigned char.
112  std::transform(
113  str.begin(), str.end(), str.begin(), [](unsigned char c) { return std::tolower(c); });
114  // Trim whitespaces
115  std::stringstream trimmer;
116  trimmer << str;
117  str.clear();
118  trimmer >> str;
119  // Match value
120  if (str == "true" || str == "on" || str == "yes") { return true; }
121  if (str == "false" || str == "off" || str == "no") { return false; }
122  throw std::invalid_argument("unknown config value " + std::string{env_var_name} + "=" +
123  std::string{env_val});
124 }
125 
126 template <>
127 inline CompatMode getenv_or(std::string_view env_var_name, CompatMode default_val)
128 {
129  auto* env_val = std::getenv(env_var_name.data());
130  if (env_val == nullptr) { return default_val; }
131  return parse_compat_mode_str(env_val);
132 }
133 
134 } // namespace detail
135 
140 class defaults {
141  private:
142  BS::thread_pool _thread_pool{get_num_threads_from_env()};
143  CompatMode _compat_mode;
144  std::size_t _task_size;
145  std::size_t _gds_threshold;
146  std::size_t _bounce_buffer_size;
147 
148  static unsigned int get_num_threads_from_env()
149  {
150  const int ret = detail::getenv_or("KVIKIO_NTHREADS", 1);
151  if (ret <= 0) {
152  throw std::invalid_argument("KVIKIO_NTHREADS has to be a positive integer greater than zero");
153  }
154  return ret;
155  }
156 
157  defaults()
158  {
159  // Determine the default value of `compat_mode`
160  {
161  _compat_mode = detail::getenv_or("KVIKIO_COMPAT_MODE", CompatMode::AUTO);
162  }
163  // Determine the default value of `task_size`
164  {
165  const ssize_t env = detail::getenv_or("KVIKIO_TASK_SIZE", 4 * 1024 * 1024);
166  if (env <= 0) {
167  throw std::invalid_argument(
168  "KVIKIO_TASK_SIZE has to be a positive integer greater than zero");
169  }
170  _task_size = env;
171  }
172  // Determine the default value of `gds_threshold`
173  {
174  const ssize_t env = detail::getenv_or("KVIKIO_GDS_THRESHOLD", 1024 * 1024);
175  if (env < 0) {
176  throw std::invalid_argument("KVIKIO_GDS_THRESHOLD has to be a positive integer");
177  }
178  _gds_threshold = env;
179  }
180  // Determine the default value of `bounce_buffer_size`
181  {
182  const ssize_t env = detail::getenv_or("KVIKIO_BOUNCE_BUFFER_SIZE", 16 * 1024 * 1024);
183  if (env <= 0) {
184  throw std::invalid_argument(
185  "KVIKIO_BOUNCE_BUFFER_SIZE has to be a positive integer greater than zero");
186  }
187  _bounce_buffer_size = env;
188  }
189  }
190 
191  KVIKIO_EXPORT static defaults* instance()
192  {
193  static defaults _instance;
194  return &_instance;
195  }
196 
197  public:
216  [[nodiscard]] static CompatMode compat_mode() { return instance()->_compat_mode; }
217 
226  static void compat_mode_reset(CompatMode compat_mode) { instance()->_compat_mode = compat_mode; }
227 
238  {
239  if (compat_mode == CompatMode::AUTO) {
240  static auto inferred_compat_mode_for_auto = []() -> CompatMode {
241  return is_cufile_available() ? CompatMode::OFF : CompatMode::ON;
242  }();
243  return inferred_compat_mode_for_auto;
244  }
245  return compat_mode;
246  }
247 
264  {
265  return compat_mode == CompatMode::ON ||
266  (compat_mode == CompatMode::AUTO &&
268  }
269 
285 
295  [[nodiscard]] static BS::thread_pool& thread_pool() { return instance()->_thread_pool; }
296 
305  [[nodiscard]] static unsigned int thread_pool_nthreads()
306  {
307  return thread_pool().get_thread_count();
308  }
309 
319  static void thread_pool_nthreads_reset(unsigned int nthreads)
320  {
321  if (nthreads == 0) {
322  throw std::invalid_argument("number of threads must be a positive integer greater than zero");
323  }
324  thread_pool().reset(nthreads);
325  }
326 
335  [[nodiscard]] static std::size_t task_size() { return instance()->_task_size; }
336 
342  static void task_size_reset(std::size_t nbytes)
343  {
344  if (nbytes == 0) {
345  throw std::invalid_argument("task size must be a positive integer greater than zero");
346  }
347  instance()->_task_size = nbytes;
348  }
349 
361  [[nodiscard]] static std::size_t gds_threshold() { return instance()->_gds_threshold; }
362 
367  static void gds_threshold_reset(std::size_t nbytes) { instance()->_gds_threshold = nbytes; }
368 
377  [[nodiscard]] static std::size_t bounce_buffer_size() { return instance()->_bounce_buffer_size; }
378 
384  static void bounce_buffer_size_reset(std::size_t nbytes)
385  {
386  if (nbytes == 0) {
387  throw std::invalid_argument(
388  "size of the bounce buffer must be a positive integer greater than zero");
389  }
390  instance()->_bounce_buffer_size = nbytes;
391  }
392 };
393 
394 } // namespace kvikio
Singleton class of default values used throughout KvikIO.
Definition: defaults.hpp:140
static std::size_t task_size()
Get the default task size used for parallel IO operations.
Definition: defaults.hpp:335
static void gds_threshold_reset(std::size_t nbytes)
Reset the default GDS threshold, which is the minimum size to use GDS (in bytes).
Definition: defaults.hpp:367
static BS::thread_pool & thread_pool()
Get the default thread pool.
Definition: defaults.hpp:295
static void bounce_buffer_size_reset(std::size_t nbytes)
Reset the size of the bounce buffer used to stage data in host memory.
Definition: defaults.hpp:384
static bool is_compat_mode_preferred()
Whether the global compatibility mode from class defaults is expected to be ON.
Definition: defaults.hpp:284
static CompatMode infer_compat_mode_if_auto(CompatMode compat_mode)
Infer the AUTO compatibility mode from the system runtime.
Definition: defaults.hpp:237
static void task_size_reset(std::size_t nbytes)
Reset the default task size used for parallel IO operations.
Definition: defaults.hpp:342
static void compat_mode_reset(CompatMode compat_mode)
Reset the value of kvikio::defaults::compat_mode().
Definition: defaults.hpp:226
static void thread_pool_nthreads_reset(unsigned int nthreads)
Reset the number of threads in the default thread pool. Waits for all currently running tasks to be c...
Definition: defaults.hpp:319
static std::size_t gds_threshold()
Get the default GDS threshold, which is the minimum size to use GDS (in bytes).
Definition: defaults.hpp:361
static unsigned int thread_pool_nthreads()
Get the number of threads in the default thread pool.
Definition: defaults.hpp:305
static bool is_compat_mode_preferred(CompatMode compat_mode)
Given a requested compatibility mode, whether it is expected to reduce to ON.
Definition: defaults.hpp:263
static std::size_t bounce_buffer_size()
Get the size of the bounce buffer used to stage data in host memory.
Definition: defaults.hpp:377
static CompatMode compat_mode()
Return whether the KvikIO library is running in compatibility mode or not.
Definition: defaults.hpp:216
CompatMode
I/O compatibility mode.
Definition: defaults.hpp:38
@ ON
Enforce POSIX I/O.
CompatMode parse_compat_mode_str(std::string_view compat_mode_str)
Parse a string into a CompatMode enum.
Definition: defaults.hpp:58