ThreadSchedule 1.0.0
Modern C++ thread management library
Loading...
Searching...
No Matches
topology.hpp
Go to the documentation of this file.
1#pragma once
2
12
13#include "scheduler_policy.hpp"
14#include <cctype>
15#include <thread>
16#include <vector>
17
18#ifndef _WIN32
19#include <fstream>
20#include <string>
21#include <unistd.h>
22#endif
23
24namespace threadschedule
25{
26
31{
32 int cpu_count{0};
33 int numa_nodes{1};
34 // Mapping: node -> list of CPUs
35 std::vector<std::vector<int>> node_to_cpus;
36};
37
42inline auto read_topology() -> CpuTopology
43{
44 CpuTopology topo;
45 topo.cpu_count = static_cast<int>(std::thread::hardware_concurrency());
46 if (topo.cpu_count <= 0)
47 topo.cpu_count = 1;
48
49#ifdef _WIN32
50 topo.numa_nodes = 1;
51 topo.node_to_cpus = {{}};
52 for (int i = 0; i < topo.cpu_count; ++i)
53 topo.node_to_cpus[0].push_back(i);
54#else
55 // Try to detect NUMA nodes via sysfs
56 int nodes = 0;
57 for (;;)
58 {
59 std::string path = "/sys/devices/system/node/node" + std::to_string(nodes);
60 if (access(path.c_str(), F_OK) != 0)
61 break;
62 ++nodes;
63 }
64 topo.numa_nodes = (nodes > 0) ? nodes : 1;
65 topo.node_to_cpus.resize(topo.numa_nodes);
66
67 if (nodes > 0)
68 {
69 for (int nodeIndex = 0; nodeIndex < nodes; ++nodeIndex)
70 {
71 std::string list_path = "/sys/devices/system/node/node" + std::to_string(nodeIndex) + "/cpulist";
72 std::ifstream in(list_path);
73 if (!in)
74 continue;
75 std::string s;
76 std::getline(in, s);
77 // Parse cpulist like "0-3,8-11"
78 size_t i = 0;
79 while (i < s.size())
80 {
81 // read number
82 int startCpu = 0;
83 bool got = false;
84 while (i < s.size() && (std::isdigit(static_cast<unsigned char>(s[i])) != 0))
85 {
86 got = true;
87 startCpu = startCpu * 10 + (s[i] - '0');
88 ++i;
89 }
90 if (!got)
91 {
92 ++i;
93 continue;
94 }
95 if (i < s.size() && s[i] == '-')
96 {
97 ++i;
98 int endCpu = 0;
99 bool gotb = false;
100 while (i < s.size() && (std::isdigit(static_cast<unsigned char>(s[i])) != 0))
101 {
102 gotb = true;
103 endCpu = endCpu * 10 + (s[i] - '0');
104 ++i;
105 }
106 if (gotb && endCpu >= startCpu)
107 {
108 for (int cpuIndex = startCpu; cpuIndex <= endCpu; ++cpuIndex)
109 topo.node_to_cpus[nodeIndex].push_back(cpuIndex);
110 }
111 }
112 else
113 {
114 topo.node_to_cpus[nodeIndex].push_back(startCpu);
115 }
116 if (i < s.size() && s[i] == ',')
117 ++i;
118 }
119 }
120 }
121 else
122 {
123 topo.node_to_cpus = {{}};
124 for (int i = 0; i < topo.cpu_count; ++i)
125 topo.node_to_cpus[0].push_back(i);
126 }
127#endif
128 return topo;
129}
130
137inline auto affinity_for_node(int node_index, int thread_index, int threads_per_node = 1) -> ThreadAffinity
138{
139 CpuTopology topo = read_topology();
140 if (topo.numa_nodes <= 0)
141 return {};
142 int const n = (node_index % topo.numa_nodes + topo.numa_nodes) % topo.numa_nodes;
143 auto const& cpus = topo.node_to_cpus[n];
144 ThreadAffinity aff;
145 if (cpus.empty())
146 return aff;
147
148 int const cpu = cpus[(thread_index) % static_cast<int>(cpus.size())];
149 aff.add_cpu(cpu);
150 // Optionally add more CPUs for the same thread if threads_per_node > 1
151 for (int k = 1; k < threads_per_node; ++k)
152 {
153 int const extra = cpus[(thread_index + k) % static_cast<int>(cpus.size())];
154 aff.add_cpu(extra);
155 }
156 return aff;
157}
158
162inline auto distribute_affinities_by_numa(size_t num_threads) -> std::vector<ThreadAffinity>
163{
164 CpuTopology topo = read_topology();
165 std::vector<ThreadAffinity> result;
166 result.reserve(num_threads);
167 for (size_t i = 0; i < num_threads; ++i)
168 {
169 int node = (topo.numa_nodes > 0) ? static_cast<int>(i % topo.numa_nodes) : 0;
170 result.push_back(affinity_for_node(node, static_cast<int>(i)));
171 }
172 return result;
173}
174
175} // namespace threadschedule
Snapshot of basic CPU/NUMA topology.
Definition topology.hpp:31
auto affinity_for_node(int node_index, int thread_index, int threads_per_node=1) -> ThreadAffinity
Build a ThreadAffinity for the given NUMA node.
Definition topology.hpp:137
auto distribute_affinities_by_numa(size_t num_threads) -> std::vector< ThreadAffinity >
Distribute thread affinities across NUMA nodes in round-robin order.
Definition topology.hpp:162
auto read_topology() -> CpuTopology
Discover basic topology. Linux: reads /sys for NUMA nodes. Windows: single node, sequential CPU indic...
Definition topology.hpp:42