Compute Library
 23.11
CpuInfo.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2021-2023 Arm Limited.
3  *
4  * SPDX-License-Identifier: MIT
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
25 
26 #include "arm_compute/core/Error.h"
27 #include "arm_compute/core/Log.h"
28 
29 #include "support/StringSupport.h"
31 
32 #include <sstream>
33 
34 #if !defined(BARE_METAL)
35 #include <algorithm>
36 #include <cstring>
37 #include <fstream>
38 #if !defined(_WIN64)
39 #include <regex.h> /* C++ std::regex takes up a lot of space in the standalone builds */
40 #include <sched.h>
41 #endif /* !defined(_WIN64) */
42 
43 #include <thread>
44 #include <unordered_map>
45 #endif /* !defined(BARE_METAL) */
46 
47 #if !defined(_WIN64)
48 #if !defined(BARE_METAL) && !defined(__APPLE__) && !defined(__OpenBSD__) && (defined(__arm__) || defined(__aarch64__))
49 #include <asm/hwcap.h> /* Get HWCAP bits from asm/hwcap.h */
50 #include <sys/auxv.h>
51 #elif defined(__APPLE__) && defined(__aarch64__)
52 #include <sys/sysctl.h>
53 #include <sys/types.h>
54 #endif /* defined(__APPLE__) && defined(__aarch64__)) */
55 #endif /* !defined(BARE_METAL) && !defined(__APPLE__) && !defined(__OpenBSD__) && (defined(__arm__) || defined(__aarch64__)) */
56 
57 #define ARM_COMPUTE_CPU_FEATURE_HWCAP_CPUID (1 << 11)
58 #define ARM_COMPUTE_GET_FEATURE_REG(var, freg) __asm __volatile("MRS %0, " #freg : "=r"(var))
59 namespace arm_compute
60 {
61 namespace cpuinfo
62 {
63 namespace
64 {
65 #if !defined(_WIN64) && !defined(BARE_METAL) && !defined(__APPLE__) && !defined(__OpenBSD__) && \
66  (defined(__arm__) || defined(__aarch64__))
67 /** Extract MIDR using CPUID information that are exposed to user-space
68  *
69  * @param[in] max_num_cpus Maximum number of possible CPUs
70  *
71  * @return std::vector<uint32_t> A list of the MIDR of each core
72  */
73 std::vector<uint32_t> midr_from_cpuid(uint32_t max_num_cpus)
74 {
75  std::vector<uint32_t> cpus;
76  for (unsigned int i = 0; i < max_num_cpus; ++i)
77  {
78  std::stringstream str;
79  str << "/sys/devices/system/cpu/cpu" << i << "/regs/identification/midr_el1";
80  std::ifstream file(str.str(), std::ios::in);
81  if (file.is_open())
82  {
83  std::string line;
84  if (bool(getline(file, line)))
85  {
86  cpus.emplace_back(support::cpp11::stoul(line, nullptr, support::cpp11::NumericBase::BASE_16));
87  }
88  }
89  }
90  return cpus;
91 }
92 
93 /** Extract MIDR by parsing the /proc/cpuinfo meta-data
94  *
95  * @param[in] max_num_cpus Maximum number of possible CPUs
96  *
97  * @return std::vector<uint32_t> A list of the MIDR of each core
98  */
99 std::vector<uint32_t> midr_from_proc_cpuinfo(int max_num_cpus)
100 {
101  std::vector<uint32_t> cpus;
102 
103  regex_t proc_regex;
104  regex_t imp_regex;
105  regex_t var_regex;
106  regex_t part_regex;
107  regex_t rev_regex;
108 
109  memset(&proc_regex, 0, sizeof(regex_t));
110  memset(&imp_regex, 0, sizeof(regex_t));
111  memset(&var_regex, 0, sizeof(regex_t));
112  memset(&part_regex, 0, sizeof(regex_t));
113  memset(&rev_regex, 0, sizeof(regex_t));
114 
115  int ret_status = 0;
116  // If "long-form" cpuinfo is present, parse that to populate models.
117  ret_status |= regcomp(&proc_regex, R"(^processor.*([[:digit:]]+)$)", REG_EXTENDED);
118  ret_status |= regcomp(&imp_regex, R"(^CPU implementer.*0x(..)$)", REG_EXTENDED);
119  ret_status |= regcomp(&var_regex, R"(^CPU variant.*0x(.)$)", REG_EXTENDED);
120  ret_status |= regcomp(&part_regex, R"(^CPU part.*0x(...)$)", REG_EXTENDED);
121  ret_status |= regcomp(&rev_regex, R"(^CPU revision.*([[:digit:]]+)$)", REG_EXTENDED);
122  ARM_COMPUTE_UNUSED(ret_status);
123  ARM_COMPUTE_ERROR_ON_MSG(ret_status != 0, "Regex compilation failed.");
124 
125  std::ifstream file("/proc/cpuinfo", std::ios::in);
126  if (file.is_open())
127  {
128  std::string line;
129  int midr = 0;
130  int curcpu = -1;
131 
132  while (bool(getline(file, line)))
133  {
134  std::array<regmatch_t, 2> match;
135  ret_status = regexec(&proc_regex, line.c_str(), 2, match.data(), 0);
136  if (ret_status == 0)
137  {
138  std::string id = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
139  int newcpu = support::cpp11::stoi(id, nullptr);
140 
141  if (curcpu >= 0 && midr == 0)
142  {
143  // Matched a new CPU ID without any description of the previous one - looks like old format.
144  return {};
145  }
146 
147  if (curcpu >= 0 && curcpu < max_num_cpus)
148  {
149  cpus.emplace_back(midr);
150  }
151  else
152  {
154  "Trying to populate a core id with id greater than the expected number of cores!");
155  }
156 
157  midr = 0;
158  curcpu = newcpu;
159 
160  continue;
161  }
162 
163  ret_status = regexec(&imp_regex, line.c_str(), 2, match.data(), 0);
164  if (ret_status == 0)
165  {
166  std::string subexp = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
167  int impv = support::cpp11::stoi(subexp, nullptr, support::cpp11::NumericBase::BASE_16);
168  midr |= (impv << 24);
169 
170  continue;
171  }
172 
173  ret_status = regexec(&var_regex, line.c_str(), 2, match.data(), 0);
174  if (ret_status == 0)
175  {
176  std::string subexp = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
177  int varv = support::cpp11::stoi(subexp, nullptr, support::cpp11::NumericBase::BASE_16);
178  midr |= (varv << 20);
179 
180  continue;
181  }
182 
183  ret_status = regexec(&part_regex, line.c_str(), 2, match.data(), 0);
184  if (ret_status == 0)
185  {
186  std::string subexp = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
187  int partv = support::cpp11::stoi(subexp, nullptr, support::cpp11::NumericBase::BASE_16);
188  midr |= (partv << 4);
189 
190  continue;
191  }
192 
193  ret_status = regexec(&rev_regex, line.c_str(), 2, match.data(), 0);
194  if (ret_status == 0)
195  {
196  std::string subexp = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
197  int regv = support::cpp11::stoi(subexp, nullptr);
198  midr |= (regv);
199  midr |= (0xf << 16);
200 
201  continue;
202  }
203  }
204 
205  if (curcpu >= 0 && curcpu < max_num_cpus)
206  {
207  cpus.emplace_back(midr);
208  }
209  else
210  {
212  "Trying to populate a core id with id greater than the expected number of cores!");
213  }
214  }
215 
216  // Free allocated memory
217  regfree(&proc_regex);
218  regfree(&imp_regex);
219  regfree(&var_regex);
220  regfree(&part_regex);
221  regfree(&rev_regex);
222 
223  return cpus;
224 }
225 
226 /** Get the maximim number of CPUs in the system by parsing /sys/devices/system/cpu/present
227  *
228  * @return int Maximum number of CPUs
229  */
230 int get_max_cpus()
231 {
232  int max_cpus = 1;
233  std::ifstream CPUspresent;
234  CPUspresent.open("/sys/devices/system/cpu/present", std::ios::in);
235  bool success = false;
236 
237  if (CPUspresent.is_open())
238  {
239  std::string line;
240 
241  if (bool(getline(CPUspresent, line)))
242  {
243  /* The content of this file is a list of ranges or single values, e.g.
244  * 0-5, or 1-3,5,7 or similar. As we are interested in the
245  * max valid ID, we just need to find the last valid
246  * delimiter ('-' or ',') and parse the integer immediately after that.
247  */
248  auto startfrom = line.begin();
249 
250  for (auto i = line.begin(); i < line.end(); ++i)
251  {
252  if (*i == '-' || *i == ',')
253  {
254  startfrom = i + 1;
255  }
256  }
257 
258  line.erase(line.begin(), startfrom);
259 
260  max_cpus = support::cpp11::stoi(line, nullptr) + 1;
261  success = true;
262  }
263  }
264 
265  // Return std::thread::hardware_concurrency() as a fallback.
266  if (!success)
267  {
268  max_cpus = std::thread::hardware_concurrency();
269  }
270  return max_cpus;
271 }
272 #elif defined(__aarch64__) && \
273  defined(__APPLE__) /* !defined(BARE_METAL) && !defined(__APPLE__) && (defined(__arm__) || defined(__aarch64__)) */
274 /** Query features through sysctlbyname
275  *
276  * @return int value queried
277  */
278 int get_hw_capability(const std::string &cap)
279 {
280  int64_t result(0);
281  size_t size = sizeof(result);
282  sysctlbyname(cap.c_str(), &result, &size, NULL, 0);
283  return result;
284 }
285 #endif /* !defined(BARE_METAL) && !defined(__APPLE__) && !defined(__OpenBSD__) && (defined(__arm__) || defined(__aarch64__)) */
286 
287 #if defined(BARE_METAL) && defined(__aarch64__)
288 uint64_t get_sve_feature_reg()
289 {
290  uint64_t svefr0 = 0;
291  __asm __volatile(".inst 0xd5380483 // mrs x3, ID_AA64ZFR0_EL1\n"
292  "MOV %0, X3"
293  : "=r"(svefr0)
294  :
295  : "x3");
296  return svefr0;
297 }
298 #endif /* defined(BARE_METAL) && defined(__aarch64__) */
299 } // namespace
300 
301 CpuInfo::CpuInfo(CpuIsaInfo isa, std::vector<CpuModel> cpus) : _isa(std::move(isa)), _cpus(std::move(cpus))
302 {
303 }
304 
306 {
307 #if !defined(_WIN64) && !defined(BARE_METAL) && !defined(__APPLE__) && !defined(__OpenBSD__) && \
308  (defined(__arm__) || defined(__aarch64__))
309  const uint32_t hwcaps = getauxval(AT_HWCAP);
310  const uint32_t hwcaps2 = getauxval(AT_HWCAP2);
311  const uint32_t max_cpus = get_max_cpus();
312 
313  // Populate midr values
314  std::vector<uint32_t> cpus_midr;
316  {
317  cpus_midr = midr_from_cpuid(max_cpus);
318  }
319  if (cpus_midr.empty())
320  {
321  cpus_midr = midr_from_proc_cpuinfo(max_cpus);
322  }
323  if (cpus_midr.empty())
324  {
325  cpus_midr.resize(max_cpus, 0);
326  }
327 
328  // Populate isa (Assume homogeneous ISA specification)
329  CpuIsaInfo isa = init_cpu_isa_from_hwcaps(hwcaps, hwcaps2, cpus_midr.back());
330 
331  // Convert midr to models
332  std::vector<CpuModel> cpus_model;
333  std::transform(std::begin(cpus_midr), std::end(cpus_midr), std::back_inserter(cpus_model),
334  [](uint32_t midr) -> CpuModel { return midr_to_model(midr); });
335 
336  CpuInfo info(isa, cpus_model);
337  return info;
338 
339 #elif (BARE_METAL) && \
340  defined( \
341  __aarch64__) /* !defined(BARE_METAL) && !defined(__APPLE__) && !defined(__OpenBSD__) && (defined(__arm__) || defined(__aarch64__)) */
342 
343  // Assume single CPU in bare metal mode. Just read the ID register and feature bits directly.
344  uint64_t isar0 = 0, isar1 = 0, pfr0 = 0, pfr1 = 0, svefr0 = 0, midr = 0;
345  ARM_COMPUTE_GET_FEATURE_REG(isar0, ID_AA64ISAR0_EL1);
346  ARM_COMPUTE_GET_FEATURE_REG(isar1, ID_AA64ISAR1_EL1);
347  ARM_COMPUTE_GET_FEATURE_REG(pfr0, ID_AA64PFR0_EL1);
348  ARM_COMPUTE_GET_FEATURE_REG(pfr1, ID_AA64PFR1_EL1);
349  ARM_COMPUTE_GET_FEATURE_REG(midr, MIDR_EL1);
350  if ((pfr0 >> 32) & 0xf)
351  {
352  svefr0 = get_sve_feature_reg();
353  }
354 
355  CpuIsaInfo isa = init_cpu_isa_from_regs(isar0, isar1, pfr0, pfr1, svefr0, midr);
356  std::vector<CpuModel> cpus_model(1, midr_to_model(midr));
357  CpuInfo info(isa, cpus_model);
358  return info;
359 #elif defined(__aarch64__) && defined(__APPLE__) /* #elif(BARE_METAL) && defined(__aarch64__) */
360  int ncpus = get_hw_capability("hw.perflevel0.logicalcpu");
361  CpuIsaInfo isainfo;
362  std::vector<CpuModel> cpus_model(ncpus);
363  isainfo.neon = get_hw_capability("hw.optional.neon");
364  isainfo.fp16 = get_hw_capability("hw.optional.neon_fp16");
365  isainfo.dot = get_hw_capability("hw.optional.arm.FEAT_DotProd");
366  CpuInfo info(isainfo, cpus_model);
367  return info;
368 #else /* #elif defined(__aarch64__) && defined(__APPLE__) */
369  CpuInfo info(CpuIsaInfo(), {CpuModel::GENERIC});
370  return info;
371 #endif /* !defined(BARE_METAL) && !defined(__APPLE__) && !defined(__OpenBSD__) && (defined(__arm__) || defined(__aarch64__)) */
372 }
373 
374 CpuModel CpuInfo::cpu_model(uint32_t cpuid) const
375 {
376  if (cpuid < _cpus.size())
377  {
378  return _cpus[cpuid];
379  }
380  return CpuModel::GENERIC;
381 }
382 
384 {
385 #if defined(_WIN64) || defined(BARE_METAL) || defined(__APPLE__) || defined(__OpenBSD__) || \
386  (!defined(__arm__) && !defined(__aarch64__))
387  return cpu_model(0);
388 #else /* defined(BARE_METAL) || defined(__APPLE__) || defined(__OpenBSD__) || (!defined(__arm__) && !defined(__aarch64__)) */
389  return cpu_model(sched_getcpu());
390 #endif /* defined(BARE_METAL) || defined(__APPLE__) || defined(__OpenBSD__) || (!defined(__arm__) && !defined(__aarch64__)) */
391 }
392 
393 uint32_t CpuInfo::num_cpus() const
394 {
395  return _cpus.size();
396 }
397 
399 {
400  unsigned int num_threads_hint = 1;
401 
402 #if !defined(BARE_METAL) && !defined(_WIN64) && !defined(ARM_COMPUTE_DISABLE_THREADS_HINT)
403  std::vector<std::string> cpus;
404  cpus.reserve(64);
405 
406  // CPU part regex
407  regex_t cpu_part_rgx;
408  memset(&cpu_part_rgx, 0, sizeof(regex_t));
409  int ret_status = regcomp(&cpu_part_rgx, R"(.*CPU part.+/?\:[[:space:]]+([[:alnum:]]+).*)", REG_EXTENDED);
410  ARM_COMPUTE_UNUSED(ret_status);
411  ARM_COMPUTE_ERROR_ON_MSG(ret_status != 0, "Regex compilation failed.");
412 
413  // Read cpuinfo and get occurrence of each core
414  std::ifstream cpuinfo_file("/proc/cpuinfo", std::ios::in);
415  if (cpuinfo_file.is_open())
416  {
417  std::string line;
418  while (bool(getline(cpuinfo_file, line)))
419  {
420  std::array<regmatch_t, 2> match;
421  if (regexec(&cpu_part_rgx, line.c_str(), 2, match.data(), 0) == 0)
422  {
423  cpus.emplace_back(line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so)));
424  }
425  }
426  }
427  regfree(&cpu_part_rgx);
428 
429  // Get min number of threads
430  std::sort(std::begin(cpus), std::end(cpus));
431  auto least_frequent_cpu_occurences = [](const std::vector<std::string> &cpus) -> uint32_t
432  {
433  std::unordered_map<std::string, uint32_t> cpus_freq;
434  for (const auto &cpu : cpus)
435  {
436  cpus_freq[cpu]++;
437  }
438 
439  uint32_t vmin = cpus.size() + 1;
440  for (const auto &cpu_freq : cpus_freq)
441  {
442  vmin = std::min(vmin, cpu_freq.second);
443  }
444  return vmin;
445  };
446 
447  // Set thread hint
448  num_threads_hint = cpus.empty() ? std::thread::hardware_concurrency() : least_frequent_cpu_occurences(cpus);
449 #endif /* !defined(BARE_METAL) && !defined(_WIN64) && !defined(ARM_COMPUTE_DISABLE_THREADS_HINT) */
450 
451  return num_threads_hint;
452 }
453 } // namespace cpuinfo
454 } // namespace arm_compute
StringSupport.h
arm_compute::cpuinfo::CpuIsaInfo
CPU ISA (Instruction Set Architecture) information.
Definition: CpuIsaInfo.h:37
arm_compute::cpuinfo::CpuIsaInfo::neon
bool neon
Definition: CpuIsaInfo.h:40
arm_compute::CPUModel
CPUModel
CPU models types.
Definition: CPPTypes.h:59
arm_compute::support::cpp11::NumericBase::BASE_16
@ BASE_16
arm_compute::cpuinfo::CpuIsaInfo::dot
bool dot
Definition: CpuIsaInfo.h:52
arm_compute::cpuinfo::CpuIsaInfo::fp16
bool fp16
Definition: CpuIsaInfo.h:47
arm_compute::cpuinfo::CpuInfo::cpu_model
CpuModel cpu_model() const
Definition: CpuInfo.cpp:383
caffe_mnist_image_extractor.str
str
Definition: caffe_mnist_image_extractor.py:21
Error.h
arm_compute::wrapper::vmin
uint8x8_t vmin(const uint8x8_t &a, const uint8x8_t &b)
Definition: min.h:39
arm_compute::cpuinfo::midr_to_model
CpuModel midr_to_model(uint32_t midr)
Extract the model type from the MIDR value.
Definition: CpuModel.cpp:80
ARM_COMPUTE_CPU_FEATURE_HWCAP_CPUID
#define ARM_COMPUTE_CPU_FEATURE_HWCAP_CPUID
Definition: CpuInfo.cpp:57
Log.h
arm_compute::cpuinfo::CpuInfo::CpuInfo
CpuInfo()=default
Default constructor.
ARM_COMPUTE_ERROR_ON_MSG
#define ARM_COMPUTE_ERROR_ON_MSG(cond, msg)
Definition: Error.h:456
arm_compute::cpuinfo::CpuInfo::build
static CpuInfo build()
CpuInfo builder function from system related information.
Definition: CpuInfo.cpp:305
arm_compute::cpuinfo::init_cpu_isa_from_regs
CpuIsaInfo init_cpu_isa_from_regs(uint64_t isar0, uint64_t isar1, uint64_t pfr0, uint64_t pfr1, uint64_t svefr0, uint64_t midr)
Identify ISA related information through register information.
Definition: CpuIsaInfo.cpp:155
arm_compute::cpuinfo::CpuInfo
Aggregate class that contains CPU related information.
Definition: CpuInfo.h:44
ARM_COMPUTE_UNUSED
#define ARM_COMPUTE_UNUSED(...)
To avoid unused variables warnings.
Definition: Error.h:151
arm_compute::support::cpp11::stoul
unsigned long stoul(const std::string &str, std::size_t *pos=0, NumericBase base=NumericBase::BASE_10)
Convert string values to unsigned long.
Definition: StringSupport.h:91
ARM_COMPUTE_LOG_INFO_MSG_CORE
#define ARM_COMPUTE_LOG_INFO_MSG_CORE(msg)
Log information level message to the core system logger.
Definition: Log.h:87
arm_compute::cpuinfo::CpuInfo::isa
const CpuIsaInfo & isa() const
Definition: CpuInfo.h:111
ToolchainSupport.h
arm_compute
Copyright (c) 2017-2023 Arm Limited.
Definition: introduction.dox:24
arm_compute::support::cpp11::stoi
int stoi(const std::string &str, std::size_t *pos=0, NumericBase base=NumericBase::BASE_10)
Convert string values to integer.
Definition: StringSupport.h:55
ARM_COMPUTE_GET_FEATURE_REG
#define ARM_COMPUTE_GET_FEATURE_REG(var, freg)
Definition: CpuInfo.cpp:58
CpuInfo.h
arm_compute::mlgo::parser::end
void end(TokenStream &in, bool &valid)
Definition: MLGOParser.cpp:283
arm_compute::cpuinfo::CpuInfo::num_cpus
uint32_t num_cpus() const
Definition: CpuInfo.cpp:393
arm_compute::test::validation::info
ScaleKernelInfo info(interpolation_policy, default_border_mode, PixelValue(), sampling_policy, false)
arm_compute::cpuinfo::init_cpu_isa_from_hwcaps
CpuIsaInfo init_cpu_isa_from_hwcaps(uint32_t hwcaps, uint32_t hwcaps2, uint32_t midr)
Identify ISA related information through system information.
Definition: CpuIsaInfo.cpp:142
isa
cpuinfo::CpuIsaInfo isa
Definition: NEFuseBatchNormalizationKernel.cpp:52
arm_compute::cpuinfo::num_threads_hint
uint32_t num_threads_hint()
Some systems have both big and small cores, this fuction computes the minimum number of cores that ar...
Definition: CpuInfo.cpp:398