VeloGraphX
High-performance dynamic graph analytics in C++20
Loading...
Searching...
No Matches
numa_policy.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <cerrno>
6#include <cstddef>
7#include <cstdint>
8#include <optional>
9#include <string>
10#include <vector>
11
12#if defined(__linux__)
13#include <pthread.h>
14#include <sched.h>
15#include <sys/mman.h>
16#include <sys/syscall.h>
17#include <unistd.h>
18#if __has_include(<linux/mempolicy.h>)
19#include <linux/mempolicy.h>
20#endif
21#endif
22
23namespace velographx {
24
27 std::size_t worker_index{0};
28 std::optional<std::size_t> node_id;
29 std::optional<std::size_t> cpu_id;
30};
31
33 void* data{nullptr};
34 std::size_t bytes{0};
36 std::optional<std::size_t> node_id;
38};
39
41 std::size_t worker_index) {
42 NumaPlacement placement{mode, worker_index, std::nullopt, std::nullopt};
43 if (mode == NumaMode::off || info.topology.empty()) return placement;
44
45 const auto node_index = worker_index % info.topology.size();
46 const auto& node = info.topology[node_index];
47 placement.node_id = node.id;
48 if (!node.cpus.empty()) {
49 const auto local_worker = worker_index / info.topology.size();
50 placement.cpu_id = node.cpus[local_worker % node.cpus.size()];
51 }
52 return placement;
53}
54
55inline bool pin_current_thread_to_cpu(std::size_t cpu_id) noexcept {
56#if defined(__linux__)
57 if (cpu_id >= CPU_SETSIZE) return false;
58 cpu_set_t cpuset;
59 CPU_ZERO(&cpuset);
60 CPU_SET(cpu_id, &cpuset);
61 return pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset) == 0;
62#else
63 (void)cpu_id;
64 return false;
65#endif
66}
67
68inline bool apply_numa_placement(const NumaPlacement& placement) noexcept {
69 if (!placement.cpu_id.has_value()) return placement.mode == NumaMode::off;
70 return pin_current_thread_to_cpu(*placement.cpu_id);
71}
72
73inline std::vector<NumaPlacement> plan_numa_workers(const NumaInfo& info, NumaMode mode,
74 std::size_t workers) {
75 std::vector<NumaPlacement> placements;
76 placements.reserve(workers);
77 for (std::size_t i = 0; i < workers; ++i) {
78 placements.push_back(choose_numa_placement(info, mode, i));
79 }
80 return placements;
81}
82
83inline bool linux_mbind_region(void* address, std::size_t bytes, NumaMode mode,
84 std::optional<std::size_t> node_id,
85 std::size_t max_node_id) noexcept {
86#if defined(__linux__) && defined(SYS_mbind) && defined(MPOL_BIND) && defined(MPOL_INTERLEAVE)
87 if (address == nullptr || bytes == 0 || mode == NumaMode::off) return mode == NumaMode::off;
88 const std::size_t word_bits = sizeof(unsigned long) * 8U;
89 const std::size_t maxnode = max_node_id + 1U;
90 std::vector<unsigned long> mask((maxnode + word_bits - 1U) / word_bits, 0UL);
91 if (mode == NumaMode::interleave) {
92 for (std::size_t node = 0; node <= max_node_id; ++node)
93 mask[node / word_bits] |= 1UL << (node % word_bits);
94 } else {
95 if (!node_id.has_value() || *node_id > max_node_id) return false;
96 mask[*node_id / word_bits] |= 1UL << (*node_id % word_bits);
97 }
98 const int policy = mode == NumaMode::interleave ? MPOL_INTERLEAVE : MPOL_BIND;
99 return ::syscall(SYS_mbind, address, bytes, policy, mask.data(), maxnode, 0UL) == 0;
100#else
101 (void)address; (void)bytes; (void)mode; (void)node_id; (void)max_node_id;
102 return false;
103#endif
104}
105
106inline NumaMemoryRegion allocate_numa_memory(std::size_t bytes, const NumaInfo& info,
107 NumaMode mode,
108 std::optional<std::size_t> node_id = std::nullopt) noexcept {
109 NumaMemoryRegion region{nullptr, bytes, mode, node_id, false};
110 if (bytes == 0) return region;
111#if defined(__linux__)
112 void* memory = ::mmap(nullptr, bytes, PROT_READ | PROT_WRITE,
113 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
114 if (memory == MAP_FAILED) return region;
115 region.data = memory;
116 if (mode != NumaMode::off && info.native_support && !info.topology.empty()) {
117 std::size_t max_node_id = 0;
118 for (const auto& node : info.topology) if (node.id > max_node_id) max_node_id = node.id;
119 region.native_policy_applied = linux_mbind_region(memory, bytes, mode, node_id, max_node_id);
120 }
121#else
122 (void)info;
123#endif
124 return region;
125}
126
127inline void first_touch_region(NumaMemoryRegion& region, std::size_t stride = 4096) noexcept {
128 if (region.data == nullptr || region.bytes == 0) return;
129 if (stride == 0) stride = 4096;
130 auto* bytes = static_cast<volatile std::uint8_t*>(region.data);
131 for (std::size_t offset = 0; offset < region.bytes; offset += stride) bytes[offset] = 0;
132 bytes[region.bytes - 1] = 0;
133}
134
135inline void release_numa_memory(NumaMemoryRegion& region) noexcept {
136#if defined(__linux__)
137 if (region.data != nullptr && region.bytes != 0) ::munmap(region.data, region.bytes);
138#endif
139 region.data = nullptr;
140 region.bytes = 0;
141 region.native_policy_applied = false;
142}
143
144inline std::string describe_numa_placement(const NumaPlacement& placement) {
145 std::string out = "mode=" + numa_mode_name(placement.mode) +
146 " worker=" + std::to_string(placement.worker_index);
147 out += placement.node_id ? " node=" + std::to_string(*placement.node_id) : " node=none";
148 out += placement.cpu_id ? " cpu=" + std::to_string(*placement.cpu_id) : " cpu=none";
149 return out;
150}
151
152} // namespace velographx
NumaPlacement choose_numa_placement(const NumaInfo &info, NumaMode mode, std::size_t worker_index)
NumaMemoryRegion allocate_numa_memory(std::size_t bytes, const NumaInfo &info, NumaMode mode, std::optional< std::size_t > node_id=std::nullopt) noexcept
std::vector< NumaPlacement > plan_numa_workers(const NumaInfo &info, NumaMode mode, std::size_t workers)
bool pin_current_thread_to_cpu(std::size_t cpu_id) noexcept
std::string describe_numa_placement(const NumaPlacement &placement)
void release_numa_memory(NumaMemoryRegion &region) noexcept
bool linux_mbind_region(void *address, std::size_t bytes, NumaMode mode, std::optional< std::size_t > node_id, std::size_t max_node_id) noexcept
void first_touch_region(NumaMemoryRegion &region, std::size_t stride=4096) noexcept
bool apply_numa_placement(const NumaPlacement &placement) noexcept
std::string numa_mode_name(NumaMode mode)
Definition numa.hpp:79
std::vector< NumaNodeInfo > topology
Definition numa.hpp:23
std::optional< std::size_t > node_id
std::optional< std::size_t > cpu_id
std::optional< std::size_t > node_id