dispenso 1.6.2
A library for task parallelism
Loading...
Searching...
No Matches
cpu_set.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
26#pragma once
27
28#include <cstdint>
29#include <string>
30#include <thread>
31#include <vector>
32
33#include <dispenso/platform.h>
34
35#if defined(__linux__) || defined(__FreeBSD__)
36#define DISPENSO_CPUSET_LINUXY
37#endif // linuxy
38
39#if defined(DISPENSO_CPUSET_LINUXY)
40#include <pthread.h>
41#include <sched.h>
42#if defined(__FreeBSD__)
43#include <pthread_np.h>
44#include <sys/cpuset.h>
45#include <sys/param.h>
46#endif
47#elif defined(_WIN32)
48#define DISPENSO_CPUSET_WINDOWS
49#endif // supported os
50
51// Note: Windows uses SetThreadGroupAffinity for binding and
52// GetLogicalProcessorInformationEx for NUMA/cache topology, behind the same public API.
53//
54// macOS does not support explicit CPU pinning. bindCurrentThread() is a no-op and
55// returns false. Topology queries return a single-node fallback.
56//
57// FreeBSD binding uses pthread_setaffinity_np, as on Linux. NUMA and cache
58// topology are read from sysctl (vm.ndomains, kern.sched.topology_spec); when
59// the kernel reports no cache levels, cache group queries return empty and
60// buildThreadGroups() falls back to contiguous chunking.
61
68#ifndef DISPENSO_MAX_GROUP_SIZE
69#define DISPENSO_MAX_GROUP_SIZE 16
70#endif
71
72namespace dispenso {
73
74namespace detail {
75#if defined(DISPENSO_CPUSET_LINUXY)
76// The native affinity-set type: cpu_set_t on Linux, cpuset_t on FreeBSD. Aliased
77// in detail rather than as a global `typedef cpuset_t cpu_set_t` so that
78// including this header does not inject a cpu_set_t name into every translation
79// unit (which could clash with a consumer's own definition).
80#if defined(__FreeBSD__)
81using CpuSetNative = ::cpuset_t;
82#else
83using CpuSetNative = ::cpu_set_t;
84#endif
85#endif // DISPENSO_CPUSET_LINUXY
86} // namespace detail
87
99constexpr int32_t kDefaultMaxGroupSize = DISPENSO_MAX_GROUP_SIZE;
100
107 std::vector<int32_t> cpus;
108 int32_t cacheId;
111};
112
113// Forward declaration; full definition follows CpuSet (ThreadGroup has a CpuSet member).
114struct ThreadGroup;
115
163class CpuSet {
164 public:
168 DISPENSO_DLL_ACCESS CpuSet();
169
173 DISPENSO_DLL_ACCESS void clear();
174
179 DISPENSO_DLL_ACCESS void add(int32_t hardwareThread);
180
186 DISPENSO_DLL_ACCESS void addRange(int32_t start, int32_t end);
187
192 DISPENSO_DLL_ACCESS void remove(int32_t hardwareThread);
193
199 DISPENSO_DLL_ACCESS void removeRange(int32_t start, int32_t end);
200
206 DISPENSO_DLL_ACCESS bool contains(int32_t hardwareThread) const;
207
211 DISPENSO_DLL_ACCESS int32_t count() const;
212
225 DISPENSO_DLL_ACCESS bool bindCurrentThread() const;
226
233 DISPENSO_DLL_ACCESS static int32_t totalNumaNodes();
234
247 DISPENSO_DLL_ACCESS static int32_t currentHardwareThread();
248
265 static constexpr uint32_t kRefreshPeriod = 32;
266 static DISPENSO_THREAD_LOCAL int32_t cachedCpu = -1;
267 static DISPENSO_THREAD_LOCAL uint32_t counter = 0;
268 if ((counter++ & (kRefreshPeriod - 1)) == 0) {
269 cachedCpu = currentHardwareThread();
270 }
271 return cachedCpu;
272 }
273
279 DISPENSO_DLL_ACCESS static const CpuSet& node(int32_t numaNode);
280
288 DISPENSO_DLL_ACCESS static const CpuSet& all();
289
301 DISPENSO_DLL_ACCESS static int32_t availableCount();
302
315 DISPENSO_DLL_ACCESS static const std::vector<CacheGroup>& l2CacheGroups();
316
329 DISPENSO_DLL_ACCESS static const std::vector<CacheGroup>& l3CacheGroups();
330
359 DISPENSO_DLL_ACCESS static std::vector<ThreadGroup> buildThreadGroups(
360 int32_t maxGroupSize = kDefaultMaxGroupSize);
361
362 private:
363#if defined(DISPENSO_CPUSET_LINUXY)
364 detail::CpuSetNative set_;
365#else
366 // Portable bitset supporting up to kMaxCpus logical processors.
367 // 1024 matches Linux cpu_set_t and covers all practical configurations.
368 // Windows Server 2025 supports up to 12,288 (192 groups x 64) but
369 // no known hardware approaches that limit. Can be increased later;
370 // dispenso does not guarantee ABI stability.
371 static constexpr int32_t kMaxCpus = 1024;
372 static constexpr int32_t kBitsPerWord = 64;
373 static constexpr int32_t kNumWords = kMaxCpus / kBitsPerWord;
374 uint64_t words_[kNumWords];
375#endif
376};
377
394 std::vector<int32_t> cpus;
396};
397
398namespace detail {
404DISPENSO_DLL_ACCESS CpuSet parseLinuxCpuList(const char* input);
405
412DISPENSO_DLL_ACCESS std::vector<CacheGroup> parseCacheGroupsFromTopologySpec(
413 const std::string& xml,
414 int cacheIndex);
415
423DISPENSO_DLL_ACCESS std::vector<ThreadGroup> buildGroupsFromCacheTopology(
424 const std::vector<CacheGroup>& l2Groups,
425 const std::vector<CacheGroup>& l3Groups,
426 int32_t maxGroupSize);
427} // namespace detail
428
429} // namespace dispenso
A set of CPU IDs for affinity manipulation and topology queries.
Definition cpu_set.h:163
static int32_t currentHardwareThreadApprox()
Approximate CPU ID for the calling thread, refreshed periodically.
Definition cpu_set.h:264
static DISPENSO_DLL_ACCESS const std::vector< CacheGroup > & l3CacheGroups()
Returns the L3 cache sharing groups.
DISPENSO_DLL_ACCESS void add(int32_t hardwareThread)
Adds a single CPU to the set.
static DISPENSO_DLL_ACCESS const CpuSet & node(int32_t numaNode)
Returns the CPU set for a specific NUMA node.
DISPENSO_DLL_ACCESS bool contains(int32_t hardwareThread) const
Tests whether a CPU is in the set.
DISPENSO_DLL_ACCESS int32_t count() const
Returns the number of CPUs in the set.
DISPENSO_DLL_ACCESS void addRange(int32_t start, int32_t end)
Adds a range of CPUs to the set.
static DISPENSO_DLL_ACCESS int32_t availableCount()
Returns the number of hardware threads available to this process.
DISPENSO_DLL_ACCESS void clear()
Removes all CPUs from the set.
DISPENSO_DLL_ACCESS bool bindCurrentThread() const
Binds the calling thread to the CPUs in this set.
static DISPENSO_DLL_ACCESS std::vector< ThreadGroup > buildThreadGroups(int32_t maxGroupSize=kDefaultMaxGroupSize)
Builds scheduling thread groups from cache topology.
static DISPENSO_DLL_ACCESS int32_t totalNumaNodes()
Returns the total number of NUMA nodes detected.
static DISPENSO_DLL_ACCESS const CpuSet & all()
Returns a CPU set containing all online CPUs.
static DISPENSO_DLL_ACCESS int32_t currentHardwareThread()
Returns the CPU ID of the calling thread's current core.
static DISPENSO_DLL_ACCESS const std::vector< CacheGroup > & l2CacheGroups()
Returns the L2 cache sharing groups.
DISPENSO_DLL_ACCESS CpuSet()
Constructs an empty CPU set.
DISPENSO_DLL_ACCESS void removeRange(int32_t start, int32_t end)
Removes a range of CPUs from the set.
DISPENSO_DLL_ACCESS void remove(int32_t hardwareThread)
Removes a single CPU from the set.
Describes a group of CPUs that share a cache level.
Definition cpu_set.h:106
std::vector< int32_t > cpus
CPU IDs in this group.
Definition cpu_set.h:107
A scheduling group of CPUs for fork-join thread pool assignment.
Definition cpu_set.h:393
std::vector< int32_t > cpus
CPU IDs in this group (sorted)
Definition cpu_set.h:394
CpuSet affinityMask
Pre-built CpuSet for binding threads in this group.
Definition cpu_set.h:395