<ol>
+ <li> New: The number of threads used by deal.II/TBB can now be limited at
+ run time. Using MPI based code using PETSc/Trilinos no longer requires you
+ to compile the library without threads. See MPI_InitFinalize and
+ MultithreadInfo::set_thread_limit for details.
+ </li>
+ (Timo Heister, 2013/03/26)
+
<li> New: The results section of step-36 now explains how to use ARPACK
as an alternative to SLEPc as eigenvalue solver.
<br>
* A class that is used to initialize the
* MPI system at the beginning of a
* program and to shut it down again at
- * the end.
+ * the end. It also allows you to control
+ * the number threads used in each MPI
+ * task.
*
* If deal.II is configured with PETSc,
* the library will also be initialized
* constructor can only be called once
* in a program, since MPI cannot be
* initialized twice.
+ *
+ * This constructor sets max_num_threads
+ * to 1 (see other constructor).
*/
MPI_InitFinalize (int &argc,
- char ** &argv);
+ char ** &argv) /*DEAL_II_DEPRECATED*/;
+ /**
+ * Initialize MPI (and optionally PETSc)
+ * and set the number of threads used by deal.II (and TBB) to the given
+ * parameter. If set to numbers::invalid_unsigned_int, the number
+ * of threads is determined by TBB. When in doubt, set this value
+ * to 1.
+ */
+ MPI_InitFinalize (int &argc,
+ char ** &argv,
+ unsigned int max_num_threads);
/**
* Destructor. Calls
* <tt>MPI_Finalize()</tt> in
* be called at destruction.
*/
const bool owns_mpi;
+
+
+ /**
+ * Called by the constructors.
+ */
+ void do_init(int &argc,
+ char ** &argv,
+ unsigned int max_num_threads);
};
namespace internal
#include <deal.II/base/config.h>
+#include <deal.II/base/types.h>
#include <deal.II/base/exceptions.h>
DEAL_II_NAMESPACE_OPEN
*/
static std::size_t memory_consumption ();
+ /**
+ * Sets the maximum number of threads to be used
+ * to the minimum of the environment variable DEAL_II_NUM_THREADS
+ * and the given parameter (if not the default value). This
+ * affects the initialization of the TBB. If neither is given, the
+ * default from TBB is used (based on the number of cores in the system).
+ *
+ * This routine is called automatically by MPI_InitFinalize. Due to
+ * limitations in the way TBB can be controlled, only the first call to this
+ * method will have any effect. Use the parameter of the MPI_InitFinalize
+ * if you have an MPI based code.
+ */
+ void set_thread_limit(const unsigned int max_threads = numbers::invalid_unsigned_int);
+
+
+ /**
+ * Returns if the TBB is running using a single thread either
+ * because of thread affinity or because it is set via a call
+ * to set_thread_limit. This is used in the PETScWrappers to
+ * avoid using the interface that is not thread-safe.
+ */
+ bool is_running_single_threaded();
/**
* Exception
*/
* one.
*/
static unsigned int get_n_cpus();
+
+ /**
+ * variable representing the maximum number of threads
+ */
+ unsigned int n_max_threads;
};
#include <deal.II/base/utilities.h>
#include <deal.II/base/exceptions.h>
#include <deal.II/lac/vector_memory.h>
+#include <deal.II/base/multithread_info.h>
#include <cstddef>
#include <iostream>
+ MPI_InitFinalize::MPI_InitFinalize (int &argc,
+ char ** &argv,
+ unsigned int max_num_threads)
+ :
+ owns_mpi (true)
+ {
+ do_init(argc, argv, max_num_threads);
+ }
+
+
+
MPI_InitFinalize::MPI_InitFinalize (int &argc,
char ** &argv)
:
owns_mpi (true)
+ {
+ do_init(argc, argv, 1);
+ }
+
+
+ void
+ MPI_InitFinalize::do_init(int &argc,
+ char ** &argv,
+ unsigned int max_num_threads)
{
static bool constructor_has_already_run = false;
Assert (constructor_has_already_run == false,
#endif
constructor_has_already_run = true;
+
+ //set maximum number of threads:
+ multithread_info.set_thread_limit(max_num_threads);
}
#include <deal.II/base/multithread_info.h>
+#include <deal.II/base/utilities.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
# include <sys/sysctl.h>
#endif
+#ifdef DEAL_II_WITH_THREADS
-DEAL_II_NAMESPACE_OPEN
+# include <deal.II/base/thread_management.h>
+# include <tbb/task_scheduler_init.h>
+#endif
+DEAL_II_NAMESPACE_OPEN
#ifdef DEAL_II_WITH_THREADS
# endif
+void MultithreadInfo::set_thread_limit(const unsigned int max_threads)
+{
+ unsigned int max_threads_env = numbers::invalid_unsigned_int;
+ char* penv;
+ penv = getenv ("DEAL_II_NUM_THREADS");
+
+ if (penv!=NULL)
+ max_threads_env = Utilities::string_to_int(std::string(penv));
+
+ n_max_threads = std::min(max_threads, max_threads_env);
+ if (n_max_threads == numbers::invalid_unsigned_int)
+ n_max_threads = tbb::task_scheduler_init::default_num_threads();
+ else
+ {
+ static tbb::task_scheduler_init dummy (n_max_threads);
+ }
+}
+
+bool MultithreadInfo::is_running_single_threaded()
+{
+ if (n_max_threads == numbers::invalid_unsigned_int)
+ n_max_threads = tbb::task_scheduler_init::default_num_threads();
+ return n_max_threads == 1;
+}
+
#else // not in MT mode
return 1;
}
+bool MultithreadInfo::is_running_single_threaded()
+{
+ return true;
+}
+
+void MultithreadInfo::set_thread_limit(const unsigned int)
+{
+}
+
#endif