MADNESS 0.10.1
thread_binding.h
Go to the documentation of this file.
1/*
2 This file is part of MADNESS.
3
4 Copyright (C) 2007,2010 Oak Ridge National Laboratory
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 For more information please contact:
21
22 Robert J. Harrison
23 Oak Ridge National Laboratory
24 One Bethel Valley Road
25 P.O. Box 2008, MS-6367
26
27 email: harrisonrj@ornl.gov
28 tel: 865-241-3937
29 fax: 865-572-0680
30*/
31
32/**
33 \file thread_binding.h
34 \brief Startup check that the MPI launcher did not pin all threads of a rank
35 onto too few hardware threads.
36 \ingroup world
37
38 Most MPI launchers bind a rank to a single core by default (e.g. Open MPI's
39 `--bind-to core` when there are more cores than ranks on a node). A
40 multi-threaded MADNESS rank inherits that affinity mask for *every* thread it
41 spawns, so the whole rank time-shares one hardware thread. The job still runs
42 and produces correct results, but one to two orders of magnitude slower, which
43 is easy to miss. \c check_thread_binding() detects this at startup and throws
44 a \c ThreadBindingException carrying an actionable error message.
45*/
46
47#ifndef MADNESS_WORLD_THREAD_BINDING_H__INCLUDED
48#define MADNESS_WORLD_THREAD_BINDING_H__INCLUDED
49
52
53#include <memory>
54#include <string>
55#include <utility>
56#include <vector>
57
58namespace madness {
59
60 /// Description of the CPU affinity of the calling process.
61
62 /// Filled in by get_thread_binding_info(); all information is obtained from
63 /// the OS (sched_getaffinity(2) and sysfs), no external library is needed.
65 /// True if the platform exposes CPU affinity (Linux). On macOS, or if
66 /// the affinity query failed, this is false and all other members are
67 /// meaningless.
68 bool available = false;
69 /// Logical (hardware thread) ids this process is allowed to run on.
70 std::vector<int> cpus;
71 /// Distinct NUMA nodes spanned by \c cpus, sorted; empty if the NUMA
72 /// topology could not be determined.
73 std::vector<int> numa_nodes;
74 /// Number of logical CPUs configured on this host (not just allowed).
76
77 /// Number of hardware threads this process may run on.
78 std::size_t ncpu_allowed() const { return cpus.size(); }
79 };
80
81 namespace detail {
82 /// Owns the (long) diagnostic text of a ThreadBindingException.
83
84 /// \c MadnessException only stores a <tt>const char*</tt>, so the text has to
85 /// live somewhere else. It is held through a \c shared_ptr so that the copies
86 /// C++ is allowed to make while the exception is in flight keep pointing at
87 /// the same, still-alive buffer.
89 std::shared_ptr<const std::string> text;
90 explicit ThreadBindingMessage(std::string t)
91 : text(std::make_shared<const std::string>(std::move(t))) {}
92 };
93 } // namespace detail
94
95 /// Thrown by check_thread_binding() when the CPU binding cannot support the thread pool.
96
97 /// Derives from MadnessException, so existing `catch(const MadnessException&)`
98 /// handlers pick it up; \c what() returns the full multi-line diagnostic.
99 /// \note The private base must stay first in the base list -- bases are
100 /// initialized in declaration order, and MadnessException's constructor
101 /// needs the text to exist already.
103 public MadnessException {
104 public:
105 ThreadBindingException(std::string text, const int line, const char* function,
106 const char* file)
109 "CPU binding over-subscribes the hardware threads", 1,
110 line, function, file) {}
111 };
112
113 /// Query the CPU affinity mask and NUMA placement of the calling process.
114 ThreadBindingInfo get_thread_binding_info();
115
116 /// Render a list of cpu ids in compact form, e.g. {0,1,2,3,8} -> "0-3,8".
117 std::string cpu_list_to_string(const std::vector<int>& cpus);
118
119 /// Throw if the CPU binding cannot accommodate the thread pool.
120
121 /// Two conditions are checked, both of which make the calculation
122 /// pathologically slow:
123 /// - this rank runs more threads than there are hardware threads in its
124 /// affinity mask (the classic "`--bind-to core` clogs the hwthreads"
125 /// failure), and
126 /// - all ranks sharing this host together request more threads than there
127 /// are hardware threads available to them (over-subscription that
128 /// `--bind-to none` does not cure).
129 ///
130 /// In addition a non-fatal warning is issued if a rank's affinity mask
131 /// straddles several NUMA domains while several ranks share the host, since
132 /// that costs memory locality.
133 ///
134 /// The check is collective over \c comm and must be called after the thread
135 /// pool has been created. It is a no-op on platforms without an affinity
136 /// API, and can be disabled with `MAD_CHECK_BINDING=OFF`.
137 ///
138 /// The verdict is agreed on collectively, so either every rank throws or none
139 /// does -- no rank is left waiting in a collective for a rank that has bailed
140 /// out. The offending rank has already written the full diagnostic to both
141 /// \c stdout and \c stderr by the time it throws, and carries that same text
142 /// in \c what(); the remaining ranks throw a one-line message pointing at it,
143 /// so the diagnostic is not repeated once per rank.
144 ///
145 /// \param[in] comm the communicator MADNESS was initialized with
146 /// \param[in] nthread_app number of application threads per rank, i.e. the
147 /// thread pool plus the main thread (the MPI communication thread
148 /// is deliberately not counted, see the implementation)
149 /// \throws ThreadBindingException if the binding over-subscribes the hardware
151
152} // namespace madness
153
154#endif // MADNESS_WORLD_THREAD_BINDING_H__INCLUDED
Wrapper around MPI_Comm. Has a shallow copy constructor; use Create(Get_group()) for deep copy.
Definition safempi.h:497
Base class for exceptions thrown in MADNESS.
Definition madness_exception.h:66
const char * function
Function where the exception occurred.
Definition madness_exception.h:72
const int line
Line number where the exception occurred.
Definition madness_exception.h:71
Thrown by check_thread_binding() when the CPU binding cannot support the thread pool.
Definition thread_binding.h:103
ThreadBindingException(std::string text, const int line, const char *function, const char *file)
Definition thread_binding.h:105
Defines madness::MadnessException for exception handling.
Definition potentialmanager.cc:41
Namespace for all elements and tools of MADNESS.
Definition DFParameters.h:10
std::string cpu_list_to_string(const std::vector< int > &cpus)
Render a list of cpu ids in compact form, e.g. {0,1,2,3,8} -> "0-3,8".
Definition thread_binding.cc:201
void check_thread_binding(const SafeMPI::Intracomm &comm, const int nthread_app)
Throw if the CPU binding cannot accommodate the thread pool.
Definition thread_binding.cc:251
ThreadBindingInfo get_thread_binding_info()
Query the CPU affinity mask and NUMA placement of the calling process.
Definition thread_binding.cc:220
static XNonlinearSolver< std::vector< Function< T, NDIM > >, T, vector_function_allocator< T, NDIM > > nonlinear_vector_solver(World &world, const long nvec)
Definition nonlinsol.h:371
Definition mraimpl.h:51
Serializes calls to MPI in case it does not support THREAD_MULTIPLE.
Description of the CPU affinity of the calling process.
Definition thread_binding.h:64
int ncpu_configured
Number of logical CPUs configured on this host (not just allowed).
Definition thread_binding.h:75
std::size_t ncpu_allowed() const
Number of hardware threads this process may run on.
Definition thread_binding.h:78
std::vector< int > numa_nodes
Definition thread_binding.h:73
std::vector< int > cpus
Logical (hardware thread) ids this process is allowed to run on.
Definition thread_binding.h:70
bool available
Definition thread_binding.h:68
Owns the (long) diagnostic text of a ThreadBindingException.
Definition thread_binding.h:88
std::shared_ptr< const std::string > text
Definition thread_binding.h:89
ThreadBindingMessage(std::string t)
Definition thread_binding.h:90