26constexpr auto AlignedCeil(std::size_t size, std::size_t page_size)
noexcept -> std::size_t {
33 if ((page_size & (page_size - 1)) != 0) {
38 return ((size + page_size - 1) / page_size) * page_size;
43 return numa_pagesize();
47 return numa_num_configured_nodes();
51 int distance = numa_distance(node1, node2);
52 return distance == 0 ? std::nullopt : std::optional<int>{distance};
56 int node = numa_node_of_cpu(cpu);
57 return node == -1 ? std::nullopt : std::optional<int>{node};
60std::error_code
MemLock(
void const* addr, std::size_t len,
LockFlag flag)
noexcept {
61 if (mlock2(addr, len,
static_cast<std::underlying_type<LockFlag>::type
>(flag)) != 0) {
62 return std::make_error_code(
static_cast<std::errc
>(errno));
67std::error_code
MemUnlock(
void const* addr, std::size_t len)
noexcept {
68 if (munlock(addr, len) != 0) {
69 return std::make_error_code(
static_cast<std::errc
>(errno));
75 if (mlockall(
static_cast<std::underlying_type<LockAllFlag>::type
>(flags)) != 0) {
76 return std::make_error_code(
static_cast<std::errc
>(errno));
82 if (munlockall() != 0) {
83 return std::make_error_code(
static_cast<std::errc
>(errno));
90 return Allocate(size, policy, MemPolicyFlag::None, ec);
96 std::error_code& ec)
noexcept {
97 auto ptr = mmap(
nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
98 if (ptr == MAP_FAILED) {
99 ec = std::make_error_code(
static_cast<std::errc
>(errno));
103 ec =
Apply(ptr, size, policy, flags);
105 (void)munmap(ptr, size);
113 auto ptr =
Allocate(size, policy, ec);
115 assert(ptr ==
nullptr);
116 throw std::system_error(ec);
123 auto ptr =
Allocate(size, policy, flags, ec);
125 assert(ptr ==
nullptr);
126 throw std::system_error(ec);
131void Free(
void* ptr, std::size_t size, std::error_code& ec)
noexcept {
132 if (munmap(ptr, size) == -1) {
133 ec = std::make_error_code(
static_cast<std::errc
>(errno));
139void Free(
void* ptr, std::size_t size) {
143 throw std::system_error(ec);
148 : m_size(
static_cast<std::size_t
>(preset)) {
155 throw std::invalid_argument(
"Invalid page size");
157 if (!std::has_single_bit(m_size)) {
158 throw std::invalid_argument(
"Invalid page size");
164 return std::countr_zero(page_size.Size()) << MAP_HUGE_SHIFT;
171 std::error_code& ec)
noexcept {
173 auto alloc_size = AlignedCeil(size, page_size.Size());
176 auto ptr = mmap(
nullptr,
178 PROT_READ | PROT_WRITE,
179 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | tlb_bits,
182 if (ptr == MAP_FAILED) {
183 ec = std::make_error_code(
static_cast<std::errc
>(errno));
187 ec =
Apply(ptr, alloc_size, policy, flags);
189 (void)munmap(ptr, alloc_size);
200 auto* ptr =
AllocateHuge(size, page_size, policy, flags, ec);
202 throw std::system_error(ec,
"AllocateHuge failed");
209 auto const alloc_size = AlignedCeil(size, page_size.Size());
210 if (munmap(ptr, alloc_size) == -1) {
211 ec = std::make_error_code(
static_cast<std::errc
>(errno));
221 throw std::system_error(ec,
"FreeHuge failed");
226 : m_policy(std::move(policy)), m_flags(flags) {
237void* PageResource::do_allocate(std::size_t bytes, std::size_t alignment) {
238 return Allocate(bytes, m_policy, m_flags);
241void PageResource::do_deallocate(
void* p, std::size_t bytes, std::size_t alignment) {
242 return Free(p, bytes);
245bool PageResource::do_is_equal(
const std::pmr::memory_resource& other)
const noexcept {
246 if (
auto const* o =
dynamic_cast<PageResource const*
>(&other); o !=
nullptr) {
247 return GetFlags() == o->GetFlags() && GetPolicy() == o->GetPolicy();
253 : m_page_size(page_size), m_policy(std::move(policy)), m_flags(flags) {
268void* HugePageResource::do_allocate(std::size_t bytes, std::size_t alignment) {
269 return AllocateHuge(bytes, m_page_size, m_policy, m_flags);
272void HugePageResource::do_deallocate(
void* p, std::size_t bytes, std::size_t alignment) {
273 return FreeHuge(p, bytes, m_page_size);
276bool HugePageResource::do_is_equal(
const std::pmr::memory_resource& other)
const noexcept {
277 if (
auto const* o =
dynamic_cast<HugePageResource const*
>(&other); o !=
nullptr) {
278 return GetFlags() == o->GetFlags() && GetPolicy() == o->GetPolicy() &&
285 : m_upstream(std::pmr::get_default_resource()), m_flag(flag) {
290 assert(upstream !=
nullptr);
294 : m_upstream(upstream), m_flag(flag) {
295 assert(upstream !=
nullptr);
298auto LockResource::do_allocate(std::size_t bytes, std::size_t alignment) ->
void* {
299 auto* mem = m_upstream->allocate(bytes, alignment);
300 assert(mem !=
nullptr);
301 auto ec =
MemLock(mem, bytes, m_flag);
303 m_upstream->deallocate(mem, bytes, alignment);
304 throw std::system_error(ec,
"MemLock failed");
309void LockResource::do_deallocate(
void* p, std::size_t bytes, std::size_t alignment) {
310 m_upstream->deallocate(p, bytes, alignment);
313auto LockResource::do_is_equal(
const std::pmr::memory_resource& other)
const noexcept ->
bool {
314 if (
auto const* o =
dynamic_cast<LockResource const*
>(&other); o !=
nullptr) {
315 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.