24constexpr auto AlignedCeil(std::size_t size, std::size_t page_size)
noexcept -> std::size_t {
31 if ((page_size & (page_size - 1)) != 0) {
36 return ((size + page_size - 1) / page_size) * page_size;
41 return numa_pagesize();
45 return numa_num_configured_nodes();
49 int distance = numa_distance(node1, node2);
50 return distance == 0 ? std::nullopt : std::optional<int>{distance};
54 int node = numa_node_of_cpu(cpu);
55 return node == -1 ? std::nullopt : std::optional<int>{node};
58std::error_code
MemLock(
void const* addr, std::size_t len,
LockFlag flag)
noexcept {
59 if (mlock2(addr, len,
static_cast<std::underlying_type<LockFlag>::type
>(flag)) != 0) {
60 return std::make_error_code(
static_cast<std::errc
>(errno));
65std::error_code
MemUnlock(
void const* addr, std::size_t len)
noexcept {
66 if (munlock(addr, len) != 0) {
67 return std::make_error_code(
static_cast<std::errc
>(errno));
73 if (mlockall(
static_cast<std::underlying_type<LockAllFlag>::type
>(flags)) != 0) {
74 return std::make_error_code(
static_cast<std::errc
>(errno));
80 if (munlockall() != 0) {
81 return std::make_error_code(
static_cast<std::errc
>(errno));
88 return Allocate(size, policy, MemPolicyFlag::None, ec);
94 std::error_code& ec)
noexcept {
95 auto ptr = mmap(
nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
96 if (ptr == MAP_FAILED) {
97 ec = std::make_error_code(
static_cast<std::errc
>(errno));
101 ec =
Apply(ptr, size, policy, flags);
103 (void)munmap(ptr, size);
111 auto ptr =
Allocate(size, policy, ec);
113 assert(ptr ==
nullptr);
114 throw std::system_error(ec);
121 auto ptr =
Allocate(size, policy, flags, ec);
123 assert(ptr ==
nullptr);
124 throw std::system_error(ec);
129void Free(
void* ptr, std::size_t size, std::error_code& ec)
noexcept {
130 if (munmap(ptr, size) == -1) {
131 ec = std::make_error_code(
static_cast<std::errc
>(errno));
137void Free(
void* ptr, std::size_t size) {
141 throw std::system_error(ec);
146 : m_size(
static_cast<std::size_t
>(preset)) {
153 throw std::invalid_argument(
"Invalid page size");
155 if (!std::has_single_bit(m_size)) {
156 throw std::invalid_argument(
"Invalid page size");
162 return std::countr_zero(page_size.Size()) << MAP_HUGE_SHIFT;
169 std::error_code& ec)
noexcept {
171 auto alloc_size = AlignedCeil(size, page_size.Size());
174 auto ptr = mmap(
nullptr,
176 PROT_READ | PROT_WRITE,
177 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | tlb_bits,
180 if (ptr == MAP_FAILED) {
181 ec = std::make_error_code(
static_cast<std::errc
>(errno));
185 ec =
Apply(ptr, alloc_size, policy, flags);
187 (void)munmap(ptr, alloc_size);
198 auto* ptr =
AllocateHuge(size, page_size, policy, flags, ec);
200 throw std::system_error(ec,
"AllocateHuge failed");
207 auto const alloc_size = AlignedCeil(size, page_size.Size());
208 if (munmap(ptr, alloc_size) == -1) {
209 ec = std::make_error_code(
static_cast<std::errc
>(errno));
219 throw std::system_error(ec,
"FreeHuge failed");
224 : m_policy(std::move(policy)), m_flags(flags) {
235void* PageResource::do_allocate(std::size_t bytes, std::size_t alignment) {
236 return Allocate(bytes, m_policy, m_flags);
239void PageResource::do_deallocate(
void* p, std::size_t bytes, std::size_t alignment) {
240 return Free(p, bytes);
243bool PageResource::do_is_equal(
const std::pmr::memory_resource& other)
const noexcept {
244 if (
auto const* o =
dynamic_cast<PageResource const*
>(&other); o !=
nullptr) {
245 return GetFlags() == o->GetFlags() && GetPolicy() == o->GetPolicy();
251 : m_page_size(page_size), m_policy(std::move(policy)), m_flags(flags) {
266void* HugePageResource::do_allocate(std::size_t bytes, std::size_t alignment) {
267 return AllocateHuge(bytes, m_page_size, m_policy, m_flags);
270void HugePageResource::do_deallocate(
void* p, std::size_t bytes, std::size_t alignment) {
271 return FreeHuge(p, bytes, m_page_size);
274bool HugePageResource::do_is_equal(
const std::pmr::memory_resource& other)
const noexcept {
275 if (
auto const* o =
dynamic_cast<HugePageResource const*
>(&other); o !=
nullptr) {
276 return GetFlags() == o->GetFlags() && GetPolicy() == o->GetPolicy() &&
283 : m_upstream(std::pmr::get_default_resource()), m_flag(flag) {
288 assert(upstream !=
nullptr);
292 : m_upstream(upstream), m_flag(flag) {
293 assert(upstream !=
nullptr);
296auto LockResource::do_allocate(std::size_t bytes, std::size_t alignment) ->
void* {
297 auto* mem = m_upstream->allocate(bytes, alignment);
298 assert(mem !=
nullptr);
299 auto ec =
MemLock(mem, bytes, m_flag);
301 m_upstream->deallocate(mem, bytes, alignment);
302 throw std::system_error(ec,
"MemLock failed");
307void LockResource::do_deallocate(
void* p, std::size_t bytes, std::size_t alignment) {
308 m_upstream->deallocate(p, bytes, alignment);
311auto LockResource::do_is_equal(
const std::pmr::memory_resource& other)
const noexcept ->
bool {
312 if (
auto const* o =
dynamic_cast<LockResource const*
>(&other); o !=
nullptr) {
313 return GetFlags() == o->GetFlags() && GetUpstream() == o->GetUpstream();
Describes a huge page size and maintains the invariant that page size is an integral power of 2,...
HugePageSize(HugePagePreset preset) noexcept
Construct from preset page size value.
Lock memory allocated from upstream memory resource using specified LockFlag.
LockResource(LockFlag flag) noexcept
Initialize with specified flag and upstream memory resource initialized from std::pmr::get_default_re...
auto GetUpstream() const noexcept -> std::pmr::memory_resource *
Get upstream memory resource.
auto GetFlags() const noexcept -> LockFlag
Get lock flag in use.
Class representing a memory policy that can be modified and used to apply to the current thread or a ...
MemPolicy(Mode mode, Nodemask &&node_mask) noexcept
Create memory policy.
std::error_code Apply(pid_t thread, CpuAffinity const &affinity) noexcept
Apply policy to specified thread.
void FreeHuge(void *ptr, std::size_t size, HugePageSize page_size, std::error_code &ec) noexcept
Free huge pages previously allocated with AllocateHuge.
auto EncodeMmapFlags(HugePageSize page_size) noexcept -> int
Encodes page size into bit representation expected by mmap().
HugePagePreset
Preset huge page sizes.
void * AllocateHuge(std::size_t size, HugePageSize page_size, MemPolicy const &policy, MemPolicyFlag flags, std::error_code &ec) noexcept
Non-throwing version of AllocateHuge()
int GetNumNodes() noexcept
Query number of configured NUMA nodes.
std::error_code MemLock(void const *addr, std::size_t len, LockFlag flag) noexcept
Lock memory pages in the specified address range.
std::size_t GetPageSize() noexcept
Fast query of system page size.
LockAllFlag
Flags that are combined to modify behaviour of MemLockAll().
std::error_code MemUnlock(void const *addr, std::size_t len) noexcept
Unlock memory pages in the specified address range.
std::optional< int > GetNodeDistance(int node1, int node2) noexcept
Get NUMA distance between two nodes.
MemPolicyFlag
Flag that modify the behaviour of applying a memory policy to a range of memory.
void Free(void *ptr, std::size_t size, std::error_code &ec) noexcept
See group for details.
LockFlag
Mutually exclusive flags that modifies behaviour of MemLock().
void * Allocate(std::size_t size, MemPolicy const &policy, std::error_code &ec) noexcept
See group for details.
std::error_code MemUnlockAll() noexcept
Unlock all locked memory in this process.
std::error_code MemLockAll(LockAllFlag flags) noexcept
Lock all memory pages as specified by provided flags.
std::optional< int > GetNodeOfCpu(int cpu) noexcept
Get NUMA node of the given CPU.
@ PreFault
Locks pages whether they are resident or not.
Contains memory function declarations.
Contains declarations for numapp::MemPolicy.