24 return detect_linux();
27 return create_fallback();
33 if (cpu_id < 0 ||
static_cast<std::size_t
>(cpu_id) >= cpu_to_node_.size())
37 return cpu_to_node_[
static_cast<std::size_t
>(cpu_id)];
42 if (node1 < 0 || node2 < 0 ||
43 static_cast<std::size_t
>(node1) >= distances_.size() ||
44 static_cast<std::size_t
>(node2) >= distances_.size())
49 const auto& row = distances_[
static_cast<std::size_t
>(node1)];
50 if (
static_cast<std::size_t
>(node2) >= row.size())
55 return row[
static_cast<std::size_t
>(node2)];
60 int node1 = get_node_for_cpu(cpu1);
61 int node2 = get_node_for_cpu(cpu2);
63 if (node1 < 0 || node2 < 0)
68 return node1 == node2;
93 for (
const auto& node : nodes_)
95 if (node.node_id == node_id)
109 DIR* node_dir = opendir(
"/sys/devices/system/node");
112 return create_fallback();
116 std::vector<int> node_ids;
117 struct dirent* entry =
nullptr;
118 while ((entry = readdir(node_dir)) !=
nullptr)
120 std::string name = entry->d_name;
121 if (name.substr(0, 4) ==
"node" && name.length() > 4)
125 int node_id = std::stoi(name.substr(4));
126 node_ids.push_back(node_id);
136 if (node_ids.empty())
138 return create_fallback();
141 std::sort(node_ids.begin(), node_ids.end());
144 unsigned int hw_concurrency = std::thread::hardware_concurrency();
145 if (hw_concurrency == 0)
152 for (
int node_id : node_ids)
155 node.node_id = node_id;
158 std::string cpulist_path =
"/sys/devices/system/node/node" +
159 std::to_string(node_id) +
"/cpulist";
160 std::ifstream cpulist_file(cpulist_path);
161 if (cpulist_file.is_open())
164 if (std::getline(cpulist_file, line))
167 std::istringstream iss(line);
169 while (std::getline(iss, token,
','))
172 auto dash_pos = token.find(
'-');
173 if (dash_pos != std::string::npos)
177 int start = std::stoi(token.substr(0, dash_pos));
178 int end = std::stoi(token.substr(dash_pos + 1));
179 for (
int cpu = start; cpu <= end; ++cpu)
181 node.cpu_ids.push_back(cpu);
182 if (
static_cast<std::size_t
>(cpu) < topology.
cpu_to_node_.size())
184 topology.
cpu_to_node_[
static_cast<std::size_t
>(cpu)] = node_id;
197 int cpu = std::stoi(token);
198 node.cpu_ids.push_back(cpu);
199 if (
static_cast<std::size_t
>(cpu) < topology.
cpu_to_node_.size())
201 topology.
cpu_to_node_[
static_cast<std::size_t
>(cpu)] = node_id;
214 std::string meminfo_path =
"/sys/devices/system/node/node" +
215 std::to_string(node_id) +
"/meminfo";
216 std::ifstream meminfo_file(meminfo_path);
217 if (meminfo_file.is_open())
220 while (std::getline(meminfo_file, line))
222 if (line.find(
"MemTotal:") != std::string::npos)
224 std::istringstream iss(line);
226 std::size_t mem_kb = 0;
227 iss >> dummy >> dummy >> mem_kb;
228 node.memory_size_bytes = mem_kb * 1024;
234 topology.
nodes_.push_back(std::move(node));
240 for (std::size_t i = 0; i < node_ids.size(); ++i)
242 topology.
distances_[i].resize(node_ids.size(), 10);
244 std::string distance_path =
"/sys/devices/system/node/node" +
245 std::to_string(node_ids[i]) +
"/distance";
246 std::ifstream distance_file(distance_path);
247 if (distance_file.is_open())
250 if (std::getline(distance_file, line))
252 std::istringstream iss(line);
253 for (std::size_t j = 0; j < node_ids.size(); ++j)
265 if (topology.
nodes_.empty())
267 return create_fallback();
278 unsigned int hw_concurrency = std::thread::hardware_concurrency();
279 if (hw_concurrency == 0)
287 for (
unsigned int i = 0; i < hw_concurrency; ++i)
289 node.
cpu_ids.push_back(
static_cast<int>(i));
293 topology.
nodes_.push_back(std::move(node));
NUMA (Non-Uniform Memory Access) topology information.
std::size_t total_cpus_
Total CPU count.
auto get_nodes() const -> const std::vector< numa_node > &
Get all NUMA nodes.
static auto detect() -> numa_topology
Detect and return the system's NUMA topology.
auto get_cpus_for_node(int node_id) const -> std::vector< int >
Get CPUs belonging to a specific node.
auto get_distance(int node1, int node2) const -> int
Get the distance between two NUMA nodes.
auto get_node_for_cpu(int cpu_id) const -> int
Get the NUMA node for a given CPU.
std::vector< std::vector< int > > distances_
Inter-node distances.
std::vector< numa_node > nodes_
All NUMA nodes.
auto is_numa_available() const -> bool
Check if NUMA is available on this system.
static auto detect_linux() -> numa_topology
Detect topology on Linux using sysfs.
std::vector< int > cpu_to_node_
CPU ID -> NUMA node mapping.
auto cpu_count() const -> std::size_t
Get the total number of CPUs.
static auto create_fallback() -> numa_topology
Create fallback single-node topology.
auto node_count() const -> std::size_t
Get the number of NUMA nodes.
auto is_same_node(int cpu1, int cpu2) const -> bool
Check if two CPUs are on the same NUMA node.
Core threading foundation of the thread system library.
NUMA node topology detection and information.
Information about a single NUMA node.
std::vector< int > cpu_ids
CPUs belonging to this node.
std::size_t memory_size_bytes
Total memory on this node.
int node_id
NUMA node identifier.