Utopia  2
Framework for studying models of complex & adaptive systems.
parallel.hh
Go to the documentation of this file.
1 #ifndef UTOPIA_CORE_PARALLEL_HH
2 #define UTOPIA_CORE_PARALLEL_HH
3 
4 // Include one of the available headers, preferring the external one
5 #if defined(HAVE_ONEDPL)
6 #include <oneapi/dpl/execution>
7 #include <oneapi/dpl/algorithm>
8 #elif defined(USE_INTERNAL_PSTL)
9 #include <execution>
10 #endif
11 
12 // PSTL is available
13 #if defined(HAVE_ONEDPL) || defined(USE_INTERNAL_PSTL)
14 #define HAVE_PSTL
15 #endif
16 
17 // PSTL is available and parallelism is enabled
18 #if defined(HAVE_PSTL) && defined(ENABLE_PARALLEL_STL)
19 #define UTOPIA_PARALLEL // Now we're cookin'!
20 #define MAYBE_UNUSED // Make sure to get proper warnings for unused parameter
21 
22 // Inject oneDPL execution policies into std namespace to have agnostic client
23 // code
24 #if defined(HAVE_ONEDPL)
25 namespace std::execution {
26  using namespace oneapi::dpl::execution;
27 }
28 #endif
29 
30 #else
31 #define MAYBE_UNUSED [[maybe_unused]] // Avoid warning for unused parameter
32 #endif
33 
34 #include <algorithm>
35 #include <exception>
36 #include <tuple>
37 
38 #include <utopia/core/logging.hh>
40 
41 namespace Utopia
42 {
43 
50 
60 {
62 
66  seq,
68  par,
69  par_unseq
70 };
71 
73 
78 {
79 private:
81  inline static bool _enabled = false;
82 
84 
88  static std::shared_ptr<spdlog::logger> get_logger()
89  {
90  const auto log = spdlog::get(Utopia::log_core);
91  if (not log)
92  {
93  throw std::runtime_error("Cannot fetch core logger!");
94  }
95 
96  return log;
97  }
98 
99 public:
101  enum Setting
102  {
104  disabled
105  };
106 
108 
113  static void init(const DataIO::Config& cfg)
114  {
115  bool setting = false;
116 
117  // Try fetching settings on parallel execution
118  if (const YAML::Node& cfg_par = cfg["parallel_execution"])
119  {
120  setting = get_as<bool>("enabled", cfg_par);
121  }
122 
123  if (setting)
124  set(Setting::enabled);
125  else
126  set(Setting::disabled);
127  }
128 
130 
135  static void set(const Setting value)
136  {
137  _enabled = (value == Setting::enabled);
138 
139  const auto log = get_logger();
140  std::string msg = fmt::format("Parallel execution {}",
141  _enabled ? "enabled" : "disabled");
142 
143 #ifdef UTOPIA_PARALLEL
144  log->info(msg);
145 #else
146  msg += ", but settings do NOT apply";
147  if (_enabled)
148  log->warn(msg);
149  else
150  log->debug(msg);
151 #endif
152  }
153 
155 
160  static bool is_enabled() { return _enabled; }
161 
163 
167  static bool is_applied()
168  {
169 #ifdef UTOPIA_PARALLEL
170  return _enabled;
171 #else
172  return false;
173 #endif
174  }
175 };
176 
178 
203 template<class Func, class... Args>
204 auto
206  Func&& f,
207  Args&&... args)
208 {
209 #ifdef UTOPIA_PARALLEL
211  {
212  if (policy == Utopia::ExecPolicy::unseq)
213  return f(std::forward_as_tuple(std::execution::unseq, args...));
214  else if (policy == Utopia::ExecPolicy::par)
215  return f(std::forward_as_tuple(std::execution::par, args...));
216  else if (policy == Utopia::ExecPolicy::par_unseq)
217  return f(std::forward_as_tuple(std::execution::par_unseq, args...));
218  }
219 #endif
220 
221 #ifdef HAVE_PSTL
222  return f(std::forward_as_tuple(std::execution::seq, args...));
223 #else
224  return f(std::forward_as_tuple(args...));
225 #endif
226 }
227 
232 } // namespace Utopia
233 
234 namespace std
235 {
236 
319 
322 template<class InputIt, class OutputIt>
323 OutputIt
325  InputIt first,
326  InputIt last,
327  OutputIt d_first)
328 {
329  return Utopia::exec_parallel(
330  policy,
331  [](auto&& args_tpl) {
332  auto copy = [](auto&&... args){ return std::copy(args...); };
333  return std::apply(copy, args_tpl);
334  },
335  first,
336  last,
337  d_first);
338 }
339 
341 
344 template<class InputIt, class UnaryFunction>
345 void
347  InputIt first,
348  InputIt last,
349  UnaryFunction f)
350 {
352  policy,
353  [](auto&& args_tpl) {
354  auto for_each = [](auto&&... args){ std::for_each(args...); };
355  std::apply(for_each, args_tpl);
356  },
357  first,
358  last,
359  f);
360 }
361 
363 
366 template<class InputIt, class OutputIt, class UnaryOperation>
367 OutputIt
369  InputIt first1,
370  InputIt last1,
371  OutputIt d_first,
372  UnaryOperation unary_op)
373 {
374  return Utopia::exec_parallel(
375  policy,
376  [](auto&& args_tpl) {
377  auto transform = [](auto&&... args) {
378  return std::transform(args...);
379  };
380  return std::apply(transform, args_tpl);
381  },
382  first1,
383  last1,
384  d_first,
385  unary_op);
386 }
387 
389 
392 template<class InputIt1, class InputIt2, class OutputIt, class BinaryOperation>
393 OutputIt
395  InputIt1 first1,
396  InputIt1 last1,
397  InputIt2 first2,
398  OutputIt d_first,
399  BinaryOperation binary_op)
400 {
401  return Utopia::exec_parallel(
402  policy,
403  [](auto&& args_tpl) {
404  auto transform = [](auto&&... args) {
405  return std::transform(args...);
406  };
407  return std::apply(transform, args_tpl);
408  },
409  first1,
410  last1,
411  first2,
412  d_first,
413  binary_op);
414 }
415 
420 } // namespace std
421 
422 #endif // UTOPIA_CORE_PARALLEL_HH
Static information on the status of parallel execution.
Definition: parallel.hh:78
static bool is_enabled()
Query if parallel execution is currently enabled.
Definition: parallel.hh:160
static std::shared_ptr< spdlog::logger > get_logger()
Fetch the core logger.
Definition: parallel.hh:88
static bool is_applied()
Actually check if parallel features are applied at runtime.
Definition: parallel.hh:167
static void init(const DataIO::Config &cfg)
Initialize parallel features based on configuration setting.
Definition: parallel.hh:113
static void set(const Setting value)
Choose a setting for parallel execution at runtime.
Definition: parallel.hh:135
Setting
Possible settings for parallel execution.
Definition: parallel.hh:102
@ enabled
Enable parallel execution.
Definition: parallel.hh:103
OutputIt copy(const Utopia::ExecPolicy policy, InputIt first, InputIt last, OutputIt d_first)
Copy the input range to a new range.
Definition: parallel.hh:324
OutputIt transform(const Utopia::ExecPolicy policy, InputIt first1, InputIt last1, OutputIt d_first, UnaryOperation unary_op)
Apply a unary operator to a range and store the result in a new range.
Definition: parallel.hh:368
void for_each(const Utopia::ExecPolicy policy, InputIt first, InputIt last, UnaryFunction f)
Apply a function to a range.
Definition: parallel.hh:346
YAML::Node Config
Type of a variadic dictionary-like data structure used throughout Utopia.
Definition: types.hh:71
const std::string log_core
Definition: logging.hh:18
auto exec_parallel(MAYBE_UNUSED const Utopia::ExecPolicy policy, Func &&f, Args &&... args)
Call a function with an STL execution policy and arguments.
Definition: parallel.hh:205
ExecPolicy
Runtime execution policies.
Definition: parallel.hh:60
@ seq
Sequential (i.e., regular) execution.
Definition: parallel.hh:66
@ unseq
SIMD execution on single thread.
Definition: parallel.hh:67
@ par_unseq
SIMD execution on multiple threads.
Definition: parallel.hh:69
@ par
Parallel/multithreaded execution.
Definition: parallel.hh:68
Definition: agent.hh:11
Definition: parallel.hh:235
#define MAYBE_UNUSED
Definition: parallel.hh:31