Reference documentation for deal.II version GIT d9d8a449a2 2022-08-17 08:45:02+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
multithread_info.cc
Go to the documentation of this file.
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2000 - 2022 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15 
17 #include <deal.II/base/utilities.h>
18 
19 #include <algorithm>
20 #include <cstdlib> // for std::getenv
21 #include <mutex>
22 #include <thread>
23 
24 #ifdef DEAL_II_WITH_TBB
25 # ifdef DEAL_II_TBB_WITH_ONEAPI
26 # include <tbb/global_control.h>
27 # else
28 # include <tbb/task_scheduler_init.h>
29 # endif
30 #endif
31 
32 
33 #ifdef DEAL_II_WITH_TASKFLOW
35 # include <taskflow/taskflow.hpp>
37 #endif
38 
40 
41 
42 unsigned int
44 {
45  // There is a slight semantic change between our n_cores() call and the
46  // std::thread alternative: in case of an error the latter one returns 0
47  // in contrast to a 1 that n_cores() used to do. For compatibility, let's
48  // translate to our numbering scheme:
49  const unsigned int n_cores = std::thread::hardware_concurrency();
50  return n_cores == 0 ? 1 : n_cores;
51 }
52 
53 
54 void
55 MultithreadInfo::set_thread_limit(const unsigned int max_threads)
56 {
57  // set the maximal number of threads to the given value as specified
58  n_max_threads = max_threads;
59 
60  // then also see if something was given in the environment
61  {
62  if (const char *penv = std::getenv("DEAL_II_NUM_THREADS"))
63  {
64  unsigned int max_threads_env = numbers::invalid_unsigned_int;
65  try
66  {
67  max_threads_env = Utilities::string_to_int(std::string(penv));
68  }
69  catch (...)
70  {
72  false,
73  ExcMessage(
74  std::string(
75  "When specifying the <DEAL_II_NUM_THREADS> environment "
76  "variable, it needs to be something that can be interpreted "
77  "as an integer. The text you have in the environment "
78  "variable is <") +
79  penv + ">"));
80  }
81 
82  AssertThrow(max_threads_env > 0,
83  ExcMessage(
84  "When specifying the <DEAL_II_NUM_THREADS> environment "
85  "variable, it needs to be a positive number."));
86 
88  n_max_threads = std::min(n_max_threads, max_threads_env);
89  else
90  n_max_threads = max_threads_env;
91  }
92  }
93 
94  // If we have not set the number of allowed threads yet, just default to
95  // the number of available cores
98 
99 #ifdef DEAL_II_WITH_TBB
100 # ifdef DEAL_II_TBB_WITH_ONEAPI
101  // tbb::global_control is a class that affects the specified behavior of
102  // tbb during its lifetime. Thus, in order to set a global thread limit
103  // for tbb we have to maintain the object throughout the execution of the
104  // program. We do this by maintaining a static std::unique_ptr.
105  //
106  // A std::unique_ptr is a good choice here because tbb::global_control
107  // does not provide a mechanism to override its setting - we can only
108  // delete the old and replace it with a new one.
109  static std::unique_ptr<tbb::global_control> tbb_global_control;
110  tbb_global_control = std::make_unique<tbb::global_control>(
111  tbb::global_control::max_allowed_parallelism, n_max_threads);
112 
113 # else
114  // Initialize the scheduler and destroy the old one before doing so
115  static tbb::task_scheduler_init dummy(tbb::task_scheduler_init::deferred);
116  if (dummy.is_active())
117  dummy.terminate();
118  dummy.initialize(n_max_threads);
119 # endif
120 #endif
121 
122 #ifdef DEAL_II_WITH_TASKFLOW
123  executor = std::make_unique<tf::Executor>(n_max_threads);
124 #endif
125 }
126 
127 
128 
129 unsigned int
131 {
133  return n_max_threads;
134 }
135 
136 
137 
138 bool
140 {
141  return n_threads() == 1;
142 }
143 
144 
145 
146 std::size_t
148 {
149  // only simple data elements, so use sizeof operator
150  return sizeof(MultithreadInfo);
151 }
152 
153 
154 
155 void
157 {
158  static std::once_flag is_initialized;
159  std::call_once(is_initialized, []() {
161  });
162 }
163 
164 
165 
166 #ifdef DEAL_II_WITH_TASKFLOW
167 tf::Executor &
169 {
170  // This should not trigger in normal user code, because we initialize the
171  // Executor in the static DoOnce struct at the end of this file unless you
172  // ask for the Executor before this static object gets constructed.
173  Assert(
174  executor.get() != nullptr,
175  ExcMessage(
176  "Please initialize multithreading using MultithreadInfo::set_thread_limit() first."));
177  return *(executor.get());
178 }
179 
180 std::unique_ptr<tf::Executor> MultithreadInfo::executor = nullptr;
181 #endif
182 
184 
185 namespace
186 {
187  // Force the first call to set_thread_limit happen before any tasks in TBB are
188  // used. This is necessary as tbb::task_scheduler_init has no effect if TBB
189  // got automatically initialized (which happens the first time we use it).
190  struct DoOnce
191  {
192  DoOnce()
193  {
195  }
196  } do_once;
197 } // namespace
198 
static unsigned int n_max_threads
MultithreadInfo()=delete
static bool is_running_single_threaded()
static std::unique_ptr< tf::Executor > executor
static void initialize_multithreading()
static unsigned int n_cores()
static unsigned int n_threads()
static void set_thread_limit(const unsigned int max_threads=numbers::invalid_unsigned_int)
static std::size_t memory_consumption()
static tf::Executor & get_taskflow_executor()
#define DEAL_II_NAMESPACE_OPEN
Definition: config.h:442
#define DEAL_II_DISABLE_EXTRA_DIAGNOSTICS
Definition: config.h:456
#define DEAL_II_NAMESPACE_CLOSE
Definition: config.h:443
#define DEAL_II_ENABLE_EXTRA_DIAGNOSTICS
Definition: config.h:495
static ::ExceptionBase & ExcInternalError()
#define Assert(cond, exc)
Definition: exceptions.h:1473
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
Definition: exceptions.h:1583
int string_to_int(const std::string &s)
Definition: utilities.cc:608
static const unsigned int invalid_unsigned_int
Definition: types.h:201