Skip to content

Commit c7be2bc

Browse files
committed
!fixup update physical core detection code
1 parent 64b14f1 commit c7be2bc

1 file changed

Lines changed: 38 additions & 8 deletions

File tree

src/util/BatchExecutor.cpp

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,63 @@
1212
#include <fstream>
1313
#include <pthread.h>
1414
#include <sched.h>
15+
#include <set>
1516
#endif
1617

1718
namespace stellar
1819
{
1920

2021
namespace
2122
{
22-
// Returns one logical CPU id per physical core: every CPU with the smallest id
23-
// among its thread-siblings. Empty on failure or on non-Linux platforms.
23+
// Returns one logical CPU id per physical core, restricted to the CPUs this
24+
// process is actually allowed to run on. Empty on failure or on non-Linux
25+
// platforms.
26+
//
27+
// The set of usable CPUs is discovered from the thread's affinity mask, not
28+
// assumed to be the contiguous range 0..hardware_concurrency()-1.
29+
// hardware_concurrency() is only a count, and the allowed set is commonly
30+
// sparse or non-zero-based (cgroup/cpuset confinement, taskset, or offlined
31+
// CPUs). Iterating by count would read topology for, and pin to, CPUs the
32+
// process cannot use - pthread_setaffinity_np then fails with EINVAL on every
33+
// worker even though usable physical cores exist.
2434
std::vector<unsigned>
2535
physicalCoreRepresentatives()
2636
{
2737
std::vector<unsigned> res;
2838
#ifdef __linux__
29-
unsigned n = std::thread::hardware_concurrency();
30-
for (unsigned cpu = 0; cpu < n; ++cpu)
39+
cpu_set_t affinity;
40+
CPU_ZERO(&affinity);
41+
if (sched_getaffinity(0, sizeof(affinity), &affinity) != 0)
3142
{
43+
// Affinity unavailable (e.g. more than CPU_SETSIZE logical CPUs); skip
44+
// pinning rather than guess.
45+
return {};
46+
}
47+
48+
// Walk the allowed CPUs in ascending order and keep the first (hence
49+
// smallest) allowed sibling of each physical core. thread_siblings_list
50+
// begins with the smallest sibling id, which is a stable per-core
51+
// identifier independent of the affinity mask, so it lets us collapse the
52+
// logical CPUs of one core down to a single representative.
53+
std::unordered_set<unsigned> seenCores;
54+
for (unsigned cpu = 0; cpu < CPU_SETSIZE; ++cpu)
55+
{
56+
if (!CPU_ISSET(cpu, &affinity))
57+
{
58+
continue;
59+
}
3260
std::ifstream in("/sys/devices/system/cpu/cpu" + std::to_string(cpu) +
3361
"/topology/thread_siblings_list");
3462
// The list is formatted like "0,16" or "0-1"; the first (smallest)
35-
// entry is the leading integer either way.
36-
unsigned firstSibling;
37-
if (!(in >> firstSibling))
63+
// entry is the leading integer either way and identifies the core.
64+
unsigned coreId;
65+
if (!(in >> coreId))
3866
{
67+
// Topology is unreadable for a CPU we are allowed to use, so skip
68+
// pinning rather than guess.
3969
return {};
4070
}
41-
if (firstSibling == cpu)
71+
if (seenCores.insert(coreId).second)
4272
{
4373
res.push_back(cpu);
4474
}

0 commit comments

Comments
 (0)