Reference documentation for deal.II version Git ca0b05a7a7 2020-12-01 11:49:21 -0500
\(\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 - 2020 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 <thread>
22 
23 #ifdef DEAL_II_WITH_TBB
24 # include <tbb/task_scheduler_init.h>
25 #endif
26 
27 
28 #ifdef DEAL_II_WITH_TASKFLOW
30 # include <taskflow/taskflow.hpp>
32 #endif
33 
35 
36 
37 unsigned int
39 {
40  // There is a slight semantic change between our n_cores() call and the
41  // std::thread alternative: in case of an error the latter one returns 0
42  // in contrast to a 1 that n_cores() used to do. For compatibility, let's
43  // translate to our numbering scheme:
44  const unsigned int n_cores = std::thread::hardware_concurrency();
45  return n_cores == 0 ? 1 : n_cores;
46 }
47 
48 
49 void
50 MultithreadInfo::set_thread_limit(const unsigned int max_threads)
51 {
52  // set the maximal number of threads to the given value as specified
53  n_max_threads = max_threads;
54 
55  // then also see if something was given in the environment
56  {
57  if (const char *penv = std::getenv("DEAL_II_NUM_THREADS"))
58  {
59  unsigned int max_threads_env = numbers::invalid_unsigned_int;
60  try
61  {
62  max_threads_env = Utilities::string_to_int(std::string(penv));
63  }
64  catch (...)
65  {
67  false,
68  ExcMessage(
69  std::string(
70  "When specifying the <DEAL_II_NUM_THREADS> environment "
71  "variable, it needs to be something that can be interpreted "
72  "as an integer. The text you have in the environment "
73  "variable is <") +
74  penv + ">"));
75  }
76 
77  AssertThrow(max_threads_env > 0,
78  ExcMessage(
79  "When specifying the <DEAL_II_NUM_THREADS> environment "
80  "variable, it needs to be a positive number."));
81 
83  n_max_threads = std::min(n_max_threads, max_threads_env);
84  else
85  n_max_threads = max_threads_env;
86  }
87  }
88 
89  // If we have not set the number of allowed threads yet, just default to
90  // the number of available cores
93 
94 #ifdef DEAL_II_WITH_TBB
95  // Initialize the scheduler and destroy the old one before doing so
96  static tbb::task_scheduler_init dummy(tbb::task_scheduler_init::deferred);
97  if (dummy.is_active())
98  dummy.terminate();
99  dummy.initialize(n_max_threads);
100 #endif
101 
102 #ifdef DEAL_II_WITH_TASKFLOW
103  executor = std::make_unique<tf::Executor>(n_max_threads);
104 #endif
105 }
106 
107 
108 
109 unsigned int
111 {
113  return n_max_threads;
114 }
115 
116 
117 
118 bool
120 {
121  return n_threads() == 1;
122 }
123 
124 
125 
126 std::size_t
128 {
129  // only simple data elements, so use sizeof operator
130  return sizeof(MultithreadInfo);
131 }
132 
133 
134 
135 void
137 {
138  static bool done = false;
139  if (done)
140  return;
141 
143  done = true;
144 }
145 
146 #ifdef DEAL_II_WITH_TASKFLOW
147 tf::Executor &
149 {
150  // This should not trigger in normal user code, because we initialize the
151  // Executor in the static DoOnce struct at the end of this file unless you
152  // ask for the Executor before this static object gets constructed.
153  Assert(
154  executor.get() != nullptr,
155  ExcMessage(
156  "Please initialize multithreading using MultithreadInfo::set_thread_limit() first."));
157  return *(executor.get());
158 }
159 
160 std::unique_ptr<tf::Executor> MultithreadInfo::executor = nullptr;
161 #endif
162 
164 
165 namespace
166 {
167  // Force the first call to set_thread_limit happen before any tasks in TBB are
168  // used. This is necessary as tbb::task_scheduler_init has no effect if TBB
169  // got automatically initialized (which happens the first time we use it).
170  struct DoOnce
171  {
172  DoOnce()
173  {
175  }
176  } do_once;
177 } // namespace
178 
static const unsigned int invalid_unsigned_int
Definition: types.h:196
MultithreadInfo()=delete
static unsigned int n_cores()
#define AssertThrow(cond, exc)
Definition: exceptions.h:1576
static std::unique_ptr< tf::Executor > executor
#define DEAL_II_DISABLE_EXTRA_DIAGNOSTICS
Definition: config.h:385
static tf::Executor & get_taskflow_executor()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define Assert(cond, exc)
Definition: exceptions.h:1466
static void initialize_multithreading()
static bool is_running_single_threaded()
#define DEAL_II_NAMESPACE_CLOSE
Definition: config.h:372
static unsigned int n_max_threads
static std::size_t memory_consumption()
#define DEAL_II_ENABLE_EXTRA_DIAGNOSTICS
Definition: config.h:422
#define DEAL_II_NAMESPACE_OPEN
Definition: config.h:371
T min(const T &t, const MPI_Comm &mpi_communicator)
static void set_thread_limit(const unsigned int max_threads=numbers::invalid_unsigned_int)
int string_to_int(const std::string &s)
Definition: utilities.cc:609
static unsigned int n_threads()
static ::ExceptionBase & ExcInternalError()