File size: 2,536 Bytes
57c2394
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include "cpu_threads.h"

#include <algorithm>
#include <cstdlib>
#include <fstream>
#include <set>
#include <string>
#include <thread>
#include <utility>

#if defined(__APPLE__)
#include <sys/sysctl.h>
#endif

#if defined(__linux__)
#include <sched.h>
#endif

namespace orbitquant::cpu {
namespace {

int environment_thread_count() {
  char const *value = std::getenv("ORBITQUANT_CPU_THREADS");
  if (value == nullptr) {
    value = std::getenv("OMP_NUM_THREADS");
  }
  if (value == nullptr) {
    return 0;
  }
  char *end = nullptr;
  const long parsed = std::strtol(value, &end, 10);
  if (end == value || parsed <= 0) {
    return 0;
  }
  return static_cast<int>(std::min<long>(parsed, 64));
}

#if defined(__linux__)
int affinity_physical_core_count() {
  cpu_set_t affinity;
  CPU_ZERO(&affinity);
  if (sched_getaffinity(0, sizeof(affinity), &affinity) != 0) {
    return 0;
  }

  int logical_cpus = 0;
  std::set<std::pair<int, int>> physical_cores;
  for (int cpu = 0; cpu < CPU_SETSIZE; ++cpu) {
    if (!CPU_ISSET(cpu, &affinity)) {
      continue;
    }
    ++logical_cpus;
    int package = -1;
    int core = -1;
    std::ifstream package_file(
        "/sys/devices/system/cpu/cpu" + std::to_string(cpu) +
        "/topology/physical_package_id");
    std::ifstream core_file(
        "/sys/devices/system/cpu/cpu" + std::to_string(cpu) +
        "/topology/core_id");
    if (package_file >> package && core_file >> core) {
      physical_cores.emplace(package, core);
    }
  }
  return physical_cores.empty()
      ? logical_cpus
      : static_cast<int>(physical_cores.size());
}
#endif

int default_thread_count() {
#if defined(__APPLE__)
  int performance_cores = 0;
  std::size_t size = sizeof(performance_cores);
  if (sysctlbyname(
          "hw.perflevel0.physicalcpu",
          &performance_cores,
          &size,
          nullptr,
          0) == 0 &&
      performance_cores > 0) {
    return std::min(performance_cores, 64);
  }
#endif
#if defined(__linux__)
  const int affinity_cores = affinity_physical_core_count();
  if (affinity_cores > 0) {
    return std::min(affinity_cores, 64);
  }
#endif
  const unsigned hardware = std::thread::hardware_concurrency();
  return static_cast<int>(hardware == 0 ? 1 : std::min<unsigned>(hardware, 64));
}

}  // namespace

int requested_threads() {
  const int environment = environment_thread_count();
  static const int default_threads = default_thread_count();
  return environment > 0 ? environment : default_threads;
}

}  // namespace orbitquant::cpu