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
39{
40 int cpu_count{0};
41 int numa_nodes{1};
42 std::vector<std::vector<int>> node_to_cpus;
43};
44
53inline auto read_topology() -> CpuTopology
54{
55 CpuTopology topo;
56 topo.cpu_count = static_cast<int>(std::thread::hardware_concurrency());
57 if (topo.cpu_count <= 0)
58 topo.cpu_count = 1;
59
60#ifdef _WIN32
61 topo.numa_nodes = 1;
62 topo.node_to_cpus = {{}};
63 for (int i = 0; i < topo.cpu_count; ++i)
64 topo.node_to_cpus[0].push_back(i);
65#else
66 // Try to detect NUMA nodes via sysfs
67 int nodes = 0;
68 for (;;)
69 {
70 std::string path = "/sys/devices/system/node/node" + std::to_string(nodes);
71 if (access(path.c_str(), F_OK) != 0)
72 break;
73 ++nodes;
74 }
75 topo.numa_nodes = (nodes > 0) ? nodes : 1;
76 topo.node_to_cpus.resize(topo.numa_nodes);
77
78 if (nodes > 0)
79 {
80 for (int nodeIndex = 0; nodeIndex < nodes; ++nodeIndex)
81 {
82 std::string list_path = "/sys/devices/system/node/node" + std::to_string(nodeIndex) + "/cpulist";
83 std::ifstream in(list_path);
84 if (!in)
85 continue;
86 std::string s;
87 std::getline(in, s);
88 // Parse cpulist like "0-3,8-11"
89 size_t i = 0;
90 while (i < s.size())
91 {
92 // read number
93 int startCpu = 0;
94 bool got = false;
95 while (i < s.size() && (std::isdigit(static_cast<unsigned char>(s[i])) != 0))
96 {
97 got = true;
98 startCpu = startCpu * 10 + (s[i] - '0');
99 ++i;
100 }
101 if (!got)
102 {
103 ++i;
104 continue;
105 }
106 if (i < s.size() && s[i] == '-')
107 {
108 ++i;
109 int endCpu = 0;
110 bool gotb = false;
111 while (i < s.size() && (std::isdigit(static_cast<unsigned char>(s[i])) != 0))
112 {
113 gotb = true;
114 endCpu = endCpu * 10 + (s[i] - '0');
115 ++i;
116 }
117 if (gotb && endCpu >= startCpu)
118 {
119 for (int cpuIndex = startCpu; cpuIndex <= endCpu; ++cpuIndex)
120 topo.node_to_cpus[nodeIndex].push_back(cpuIndex);
121 }
122 }
123 else
124 {
125 topo.node_to_cpus[nodeIndex].push_back(startCpu);
126 }
127 if (i < s.size() && s[i] == ',')
128 ++i;
129 }
130 }
131 }
132 else
133 {
134 topo.node_to_cpus = {{}};
135 for (int i = 0; i < topo.cpu_count; ++i)
136 topo.node_to_cpus[0].push_back(i);
137 }
138#endif
139 return topo;
140}
141
151inline auto affinity_for_node(int node_index, int thread_index, int threads_per_node = 1) -> ThreadAffinity
152{
153 CpuTopology topo = read_topology();
154 if (topo.numa_nodes <= 0)
155 return {};
156 int const n = (node_index % topo.numa_nodes + topo.numa_nodes) % topo.numa_nodes;
157 auto const& cpus = topo.node_to_cpus[n];
158 ThreadAffinity aff;
159 if (cpus.empty())
160 return aff;
161
162 int const cpu = cpus[(thread_index) % static_cast<int>(cpus.size())];
163 aff.add_cpu(cpu);
164 // Optionally add more CPUs for the same thread if threads_per_node > 1
165 for (int k = 1; k < threads_per_node; ++k)
166 {
167 int const extra = cpus[(thread_index + k) % static_cast<int>(cpus.size())];
168 aff.add_cpu(extra);
169 }
170 return aff;
171}
172
182inline auto distribute_affinities_by_numa(size_t num_threads) -> std::vector<ThreadAffinity>
183{
184 CpuTopology topo = read_topology();
185 std::vector<ThreadAffinity> result;
186 result.reserve(num_threads);
187 for (size_t i = 0; i < num_threads; ++i)
188 {
189 int node = (topo.numa_nodes > 0) ? static_cast<int>(i % topo.numa_nodes) : 0;
190 result.push_back(affinity_for_node(node, static_cast<int>(i)));
191 }
192 return result;
193}
194
195} // namespace threadschedule
Manages a set of CPU indices to which a thread may be bound.
Snapshot of basic CPU/NUMA topology.
Definition topology.hpp:39
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:151
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:182
auto read_topology() -> CpuTopology
Discover basic topology. Linux: reads /sys for NUMA nodes. Windows: single node, sequential CPU indic...
Definition topology.hpp:53