NUMA++ 0.12.0
Loading...
Searching...
No Matches
mempolicy.hpp
Go to the documentation of this file.
1/**
2 * @file
3 * @ingroup numapp_mem
4 * @brief Contains declarations for numapp::MemPolicy.
5 * @copyright
6 * SPDX-FileCopyrightText: 2020-2023 European Southern Observatory (ESO)
7 *
8 * SPDX-License-Identifier: LGPL-3.0-only
9 *
10 * @defgroup numapp_mem Memory APIs
11 * @ingroup numapp
12 * @brief NUMA++ memory policy APIs
13 *
14 * See @ref memory-api-policy for an overview.
15 */
16#ifndef NUMAPP_MEMPOLICY_HPP_
17#define NUMAPP_MEMPOLICY_HPP_
18#include <numapp/config.hpp>
19
20#include <iosfwd>
21#include <memory>
22#include <numaif.h>
23#include <optional>
24#include <system_error>
25
26#include <numapp/flags.hpp>
27#include <numapp/nodemask.hpp>
28
29namespace numapp {
30class MemPolicy;
31
32/**
33 * Flag that modify the behaviour of applying a memory policy to a range of memory.
34 * @ingroup numapp_mem
35 * @headerfile <> <numapp/mempolicy.hpp>
36 * @relatesalso MemPolicy
37 */
38enum class MemPolicyFlag : unsigned {
39 /**
40 * No-op flag.
41 */
42 None = 0u,
43 /**
44 * Determines if application of memory policy should be strict and fail if MemPolicy cannot be
45 * used rather than e.g. allocate from other nodes.
46 *
47 * Cause Apply(void*, std::size_t, MemPolicy, MemPolicyFlag) to fail if existing pages don't
48 * follow the policy. If combined with Move the operation will fail if all pages couldn't be
49 * moved.
50 *
51 * This does not apply to MemPolicy::Mode::Default or MemPolicy::Mode::Interleaved.
52 */
53 Strict = MPOL_MF_STRICT,
54 /**
55 * Attempt to move pages so that they follow the policy. Can be combined with Strict to cause
56 * operation to fail if pages couldn't be moved.
57 */
58 Move = MPOL_MF_MOVE,
59 /**
60 * Attempt to move pages so that they follow the policy regardless whether another process is
61 * using the pages. This requires capability CAP_SYS_NICE.
62 *
63 * Can be combined with Strict to have the operation fail if all pages couldn't be moved.
64 */
65 MoveAll = MPOL_MF_MOVE_ALL,
66};
67
68NUMAPP_ENABLE_BITFLAG(MemPolicyFlag)
69
70namespace thisThread {
71
72/**
73 * @name Apply Memory Policy to Current Thread
74 *
75 * Applies default memory policy to calling thread or to thread stack memory.
76 * @ingroup numapp_mem
77 */
78/// @{
79
80/**
81 * Applies the default memory policy to the calling thread.
82 *
83 * @note The memory policy is only applied to new physical page allocations.
84 *
85 * @param policy memory policy to set.
86 *
87 * @manpages
88 * @manpage{set_mempolicy,2}
89 *
90 * @relatesalso numapp::MemPolicy
91 * @ingroup numapp_mem
92 */
93[[nodiscard]] std::error_code Apply(MemPolicy const& policy) noexcept;
94
95/**
96 * Convenience function that applies a memory policy to current thread stack memory.
97 *
98 * @note It relies on pthread API get the stack memory address and size. No pre-faulting is made.
99 *
100 * @param policy Memory policy to apply.
101 * @param flags combination of MemPolicyFlag values that modify the behaviour. Default is to move
102 * pages and fail if this cannot be done.
103 *
104 * @manpages
105 * @manpage{mbind,2}
106 * @relatesalso MemPolicy
107 * @ingroup numapp_mem
108 */
109[[nodiscard]] std::error_code
110ApplyStack(MemPolicy const& policy,
111 MemPolicyFlag flags = MemPolicyFlag::Move | MemPolicyFlag::Strict) noexcept;
112/// @}
113
114} // namespace thisThread
115
116/**
117 * @name Apply Memory Policy to Memory Range
118 */
119/// @{
120
121/**
122 * Applies memory policy to the memory pages that spans the range [@a address, @a adress +
123 * @a length].
124 *
125 * @note The memory policy is by default only applied to new physical page allocations. This can be
126 * modified using MemPolicyFlag::Move and MemPolicyFlag::MoveAll.
127 *
128 * Read related man-page of @c mbind() to understand the various behaviours if memory is mapped or
129 * shared.
130 *
131 * @param address start of address range.
132 * @param length length of range.
133 * @param policy Policy to apply.
134 * @param flags combination of MemPolicyFlag values that modify the behaviour.
135 * @returns 0 if successful.
136 * @returns non-zero if it failed.
137 *
138 * @manpages
139 * @manpage{mbind,2}
140 * @relatesalso MemPolicy
141 */
142[[nodiscard]] std::error_code Apply(void* address,
143 std::size_t length,
144 MemPolicy const& policy,
145 MemPolicyFlag flags) NUMAPP_NOEXCEPT;
146///@}
147
148/**
149 * Class representing a memory policy that can be modified and used to apply to the current thread
150 * or a memory range.
151 *
152 * Uses numa APIs @c set_mempolicy and @c get_mempolicy.
153 *
154 * To read the current memory policy use MemPolicy::MakeFromActive. To apply a policy use
155 * thisThread::Apply(MemPolicy const&).
156 *
157 * For CPU policy see @c numapp::CpuAffinity.
158 *
159 * @attention The memory policy is used for new physical page allocations. One way this happens is
160 * by writing to unpaged memory which triggers a page-fault that then is handled according to the
161 * policy. This means that it's possible that memory returned by an allocator like
162 * @c malloc/@c std::allocator/@c new after a sucessfull call to MemPolicy::Apply does not in
163 * fact allocate a new physical page but may reuse recently freed memory or otherwise unused, but
164 * physically allocated memory.
165 *
166 * @attention Bottom line is that MemPolicy does @a not apply directly to virtual memory allocators
167 * but on the kernel level page allocation.
168 *
169 * @headerfile <> <numapp/mempolicy.hpp>
170 * @related numapp::ScopedMemPolicy
171 * @manpages
172 * @manpage{get_mempolicy,2}
173 * @manpage{set_mempolicy,2}
174 * @ingroup numapp_mem
175 */
177public:
178 /**
179 * Memory allocation modes.
180 */
181 enum class Mode : int {
182 /**
183 * The Default mode specifies that any nondefault process memory policy be removed, so that
184 * the memory policy "falls back" to the system default policy. The system default policy is
185 * "local allocation"-- i.e., allocate memory on the node of the CPU that triggered the
186 * allocation. nodemask must be specified as NULL. If the "local node" contains no free
187 * memory, the system will attempt to allocate memory from a "near by" node.
188 *
189 * Corresponds to native mode @c MPOL_DEFAULT.
190 */
191 Default = MPOL_DEFAULT,
192 /**
193 * The Bind mode defines a strict policy that restricts memory allocation to the nodes
194 * specified in nodemask. If nodemask specifies more than one node, page allocations will
195 * come from the node with the lowest numeric node ID first, until that node contains no
196 * free memory. Allocations will then come from the node with the next highest node ID
197 * specified in nodemask and so forth, until none of the specified nodes contain free
198 * memory. Pages will not be allocated from any node not specified in the nodemask.
199 *
200 * Corresponds to native mode @c MPOL_BIND.
201 */
202 Bind = MPOL_BIND,
203 /**
204 * Interleave interleaves page allocations across the nodes specified in nodemask in
205 * numeric node ID order. This optimizes for bandwidth instead of latency by spreading out
206 * pages and memory accesses to those pages across multiple nodes. However, accesses to a
207 * single page will still be limited to the memory bandwidth of a single node.
208 *
209 * Corresponds to native mode @c MPOL_INTERLEAVE.
210 */
211 Interleave = MPOL_INTERLEAVE,
212 /**
213 * Preferred sets the preferred node for allocation. The kernel will try to allocate pages
214 * from this node first and fall back to "near by" nodes if the preferred node is low on
215 * free memory. If nodemask specifies more than one node ID, the first node in the mask will
216 * be selected as the preferred node. If the nodemask and maxnode arguments specify the
217 * empty set, then the policy specifies "local allocation" (like the system default policy
218 * discussed above).
219 *
220 * Corresponds to native mode @c MPOL_PREFERRED.
221 */
222 Preferred = MPOL_PREFERRED
223 };
224
225 /**
226 * Create memory policy.
227 *
228 * @param mode Policy mode
229 * @param node_mask
230 */
231 explicit MemPolicy(Mode mode, Nodemask&& node_mask) noexcept
232 : m_mode(mode), m_nodes(std::move(node_mask)) {
233 }
234 explicit MemPolicy(Mode mode, Nodemask const& node_mask) : m_mode(mode), m_nodes(node_mask) {
235 }
236
237 MemPolicy(MemPolicy&&) noexcept = default;
238 MemPolicy& operator=(MemPolicy&&) noexcept = default;
239 MemPolicy(MemPolicy const& rhs) = default;
240 MemPolicy& operator=(MemPolicy const& rhs) noexcept = default;
241
242 /**
243 * Creates a strict policy (using @c MPOL_BIND) to allocate all memory to the specified node.
244 *
245 * @param node Numa node.
246 * @throws std::system_error if it fails.
247 */
248 [[nodiscard]] static MemPolicy MakeBindNode(int node);
249
250 /**
251 * Create MemPolicy from mode and `nodestring` while considering currently allowed nodes.
252 *
253 *
254 * Nodestring use patterns such as:
255 *
256 * - <tt>1-5,7,10</tt>
257 * - <tt>!4-5</tt>
258 * - <tt>+0-3</tt>
259 * - <tt>all</tt>
260 *
261 * @manpages
262 * @manpage{numa,3}
263 *
264 * @param mode Memory policy mode such as @c MPOL_BIND
265 * @param nodestring Nodes to include such as <tt>1-5,7,10</tt> or <tt>all</tt>.
266 * @throws std::system_error on failure. This can also happen if the @a nodestring references a
267 * disallowed node. Use @ref MakeFromNodeStringAll to ignore this.
268 *
269 * @sa MakeFromNodeStringAll
270 */
271 [[nodiscard]] static MemPolicy MakeFromNodeString(Mode mode, char const* nodestring) {
272 return MemPolicy(mode, Nodemask::MakeFromNodestring(nodestring));
273 }
274
275 /**
276 * Create MemPolicy from mode and `nodestring` considering all nodes, whether they are allowed
277 * or not.
278 *
279 * Nodestring use patterns such as:
280 *
281 * - <tt>1-5,7,10</tt>
282 * - <tt>!4-5</tt>
283 * - <tt>+0-3</tt>
284 * - <tt>all</tt>
285 *
286 * @manpages
287 * @manpage{numa,3}
288 *
289 * @param mode Memory policy mode such as @c MPOL_BIND
290 * @param nodestring Nodes to include such as <tt>1-5,7,10</tt> or <tt>all</tt>.
291 * @throws std::system_error on failure. This can also happen if the @a nodestring references a
292 * disallowed node. Use @ref MakeFromNodeStringAll to ignore this.
293 *
294 * @sa MakeFromNodeString
295 */
296 [[nodiscard]] static MemPolicy MakeFromNodeStringAll(Mode mode, char const* nodestring) {
297 return MemPolicy(mode, Nodemask::MakeFromNodestringAll(nodestring));
298 }
299
300 /**
301 * Make from active default policy of calling thread.
302 *
303 * @throws std::system_error if it fails.
304 */
305 [[nodiscard]] static MemPolicy MakeFromActive();
306
307 /**
308 * Make from policy at address.
309 *
310 * @param address memory address to make policy from.
311 * @throws std::system_error if it fails.
312 */
313 [[nodiscard]] static MemPolicy MakeFromAddress(void* address);
314
315 /**
316 * @return true if this memory policy mode and nodes equals @a rhs.
317 */
318 [[nodiscard]] bool operator==(MemPolicy const& rhs) const noexcept {
319 return m_mode == rhs.m_mode && m_nodes == rhs.m_nodes;
320 }
321 [[nodiscard]] bool operator!=(MemPolicy const& rhs) const noexcept {
322 return !(*this == rhs);
323 }
324
325 /**
326 * @return policy mode.
327 */
328 [[nodiscard]] Mode GetMode() const noexcept {
329 return m_mode;
330 }
331
332 /**
333 * Set memory policy mode.
334 *
335 * @param mode policy mode.
336 */
337 void SetMode(Mode mode) noexcept {
338 m_mode = mode;
339 }
340
341 /**
342 * @return Node mask.
343 */
344 Nodemask const& GetNodemask() const noexcept {
345 return m_nodes;
346 }
347
348private:
349 MemPolicy() : m_mode(Mode::Default), m_nodes() {
350 }
351
352 Mode m_mode;
353 Nodemask m_nodes;
354};
355
356/**
357 * Formats @a mode and inserts it to @a os.
358 *
359 * @param os output stream to insert into.
360 * @param mode Memory policy mode to format.
361 * @returns @a os
362 *
363 * @relates MemPolicy
364 */
365std::ostream& operator<<(std::ostream& os, MemPolicy::Mode const& mode);
366
367/**
368 * Formats @a mempolicy and inserts it to @a os.
369 *
370 * @param os output stream to insert into.
371 * @param mempolicy Memory policy to format.
372 * @returns @a os
373 *
374 * @relates MemPolicy
375 */
376std::ostream& operator<<(std::ostream& os, MemPolicy const& mempolicy);
377
378/**
379 * Applies memory policy to this thread at construction and reverts to previous at destruction.
380 *
381 * @sa numapp::MemPolicy
382 *
383 * @ingroup numapp_mem
384 */
385class [[maybe_unused]] ScopedMemPolicy {
386public:
387 /**
388 * Constructs an object that applies the provided policy during construction
389 * and restores it during destruction.
390 *
391 * @throws std::system_error containing the error code if it fails to apply new policy.
392 * Attempt to restore old policy will be done.
393 */
394 explicit ScopedMemPolicy(MemPolicy const& new_policy)
395 : m_old_policy(MemPolicy::MakeFromActive()) {
396 auto ec = thisThread::Apply(new_policy);
397 if (ec) {
398 throw std::system_error(ec);
399 }
400 }
401
402 /**
403 * Restores old policy, if any.
404 */
405 ~ScopedMemPolicy() noexcept {
406 (void)Restore();
407 }
408
409 /**
410 * Initialized a scoped memory policy that does nothing
411 * and holds no state about previous policy.
412 */
413 ScopedMemPolicy() noexcept = default;
414
415 /**
416 * Swap state with another @c ScopedMemPolicy.
417 */
418 void Swap(ScopedMemPolicy& rhs) noexcept {
419 m_old_policy.swap(rhs.m_old_policy);
420 }
421
422 /**
423 * Restores memory policy that was read during construction.
424 *
425 * @returns the error that caused it to fail to apply old policy.
426 */
427 [[nodiscard]] std::error_code Restore() noexcept;
428
429private:
430 std::optional<MemPolicy> m_old_policy;
431};
432
433} // namespace numapp
434#endif // #ifndef NUMAPP_MEMPOLICY_HPP_
Class representing a memory policy that can be modified and used to apply to the current thread or a ...
std::ostream & operator<<(std::ostream &os, MemPolicy const &mempolicy)
Formats mempolicy and inserts it to os.
Mode
Memory allocation modes.
@ Bind
The Bind mode defines a strict policy that restricts memory allocation to the nodes specified in node...
@ Default
The Default mode specifies that any nondefault process memory policy be removed, so that the memory p...
@ Preferred
Preferred sets the preferred node for allocation.
@ Interleave
Interleave interleaves page allocations across the nodes specified in nodemask in numeric node ID ord...
MemPolicy(Mode mode, Nodemask &&node_mask) noexcept
Create memory policy.
Nodemask const & GetNodemask() const noexcept
bool operator==(MemPolicy const &rhs) const noexcept
static MemPolicy MakeFromActive()
Make from active default policy of calling thread.
static MemPolicy MakeFromNodeString(Mode mode, char const *nodestring)
Create MemPolicy from mode and nodestring while considering currently allowed nodes.
Mode GetMode() const noexcept
void SetMode(Mode mode) noexcept
Set memory policy mode.
static MemPolicy MakeFromAddress(void *address)
Make from policy at address.
static MemPolicy MakeBindNode(int node)
Creates a strict policy (using MPOL_BIND) to allocate all memory to the specified node.
std::ostream & operator<<(std::ostream &os, MemPolicy::Mode const &mode)
Formats mode and inserts it to os.
static MemPolicy MakeFromNodeStringAll(Mode mode, char const *nodestring)
Create MemPolicy from mode and nodestring considering all nodes, whether they are allowed or not.
std::error_code Apply(void *address, std::size_t length, MemPolicy const &policy, MemPolicyFlag flags) noexcept
Applies memory policy to the memory pages that spans the range [address, adress + length].
ScopedMemPolicy() noexcept=default
Initialized a scoped memory policy that does nothing and holds no state about previous policy.
void Swap(ScopedMemPolicy &rhs) noexcept
Swap state with another ScopedMemPolicy.
ScopedMemPolicy(MemPolicy const &new_policy)
Constructs an object that applies the provided policy during construction and restores it during dest...
std::error_code Restore() noexcept
Restores memory policy that was read during construction.
~ScopedMemPolicy() noexcept
Restores old policy, if any.
NUMA++ configuration.
Contains definitions for bitwise operators for enums.
#define NUMAPP_ENABLE_BITFLAG(enum)
Enables numapp bitwise operators in overload set for the specified enumeration enum.
Definition flags.hpp:19
std::error_code Apply(CpuAffinity const &affinity) noexcept
Apply policy to calling thread.
MemPolicyFlag
Flag that modify the behaviour of applying a memory policy to a range of memory.
Definition mempolicy.hpp:38
std::error_code ApplyStack(MemPolicy const &policy, MemPolicyFlag flags=MemPolicyFlag::Move|MemPolicyFlag::Strict) noexcept
Convenience function that applies a memory policy to current thread stack memory.
std::error_code Apply(MemPolicy const &policy) noexcept
Applies the default memory policy to the calling thread.
Contains declarations for Nodemask.
Type-safe NUMA node mask.
Definition nodemask.hpp:25
static Nodemask MakeFromNodestringAll(char const *nodestring)
Construct Nodemask from nodestring that considers does not consider current cpuset.
Definition nodemask.hpp:58
static Nodemask MakeFromNodestring(char const *nodestring)
Construct Nodemask from nodestring that considers current cpuset.
Definition nodemask.hpp:43