blob: 92ba5223c9b795c1a5fa5ac317963f8b65e50735 [file] [log] [blame]
Georgios Pinitas08302c12021-06-09 10:08:27 +01001/*
Pablo Marquez Tello6fe9eaf2024-02-29 16:36:09 +00002 * Copyright (c) 2021-2024 Arm Limited.
Georgios Pinitas08302c12021-06-09 10:08:27 +01003 *
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 */
24#include "src/common/cpuinfo/CpuInfo.h"
25
26#include "arm_compute/core/Error.h"
27#include "arm_compute/core/Log.h"
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010028
Georgios Pinitas08302c12021-06-09 10:08:27 +010029#include "support/StringSupport.h"
30#include "support/ToolchainSupport.h"
31
Omar Al Khatibf5053f72024-05-09 16:06:23 +010032#include <map>
Georgios Pinitas08302c12021-06-09 10:08:27 +010033#include <sstream>
34
35#if !defined(BARE_METAL)
36#include <algorithm>
37#include <cstring>
38#include <fstream>
Pablo Tello4e66d702022-03-07 18:20:12 +000039#if !defined(_WIN64)
Georgios Pinitas08302c12021-06-09 10:08:27 +010040#include <regex.h> /* C++ std::regex takes up a lot of space in the standalone builds */
41#include <sched.h>
Pablo Tello4e66d702022-03-07 18:20:12 +000042#endif /* !defined(_WIN64) */
43
Georgios Pinitas08302c12021-06-09 10:08:27 +010044#include <thread>
45#include <unordered_map>
46#endif /* !defined(BARE_METAL) */
47
Pablo Tello4e66d702022-03-07 18:20:12 +000048#if !defined(_WIN64)
Kevin Lo7195f712022-01-07 15:46:02 +080049#if !defined(BARE_METAL) && !defined(__APPLE__) && !defined(__OpenBSD__) && (defined(__arm__) || defined(__aarch64__))
Georgios Pinitas08302c12021-06-09 10:08:27 +010050#include <asm/hwcap.h> /* Get HWCAP bits from asm/hwcap.h */
51#include <sys/auxv.h>
Pablo Marquez Tello639f0f62022-01-21 15:25:27 +000052#elif defined(__APPLE__) && defined(__aarch64__)
53#include <sys/sysctl.h>
54#include <sys/types.h>
Pablo Tello4e66d702022-03-07 18:20:12 +000055#endif /* defined(__APPLE__) && defined(__aarch64__)) */
Kevin Lo7195f712022-01-07 15:46:02 +080056#endif /* !defined(BARE_METAL) && !defined(__APPLE__) && !defined(__OpenBSD__) && (defined(__arm__) || defined(__aarch64__)) */
Georgios Pinitas08302c12021-06-09 10:08:27 +010057
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010058#define ARM_COMPUTE_CPU_FEATURE_HWCAP_CPUID (1 << 11)
59#define ARM_COMPUTE_GET_FEATURE_REG(var, freg) __asm __volatile("MRS %0, " #freg : "=r"(var))
Georgios Pinitas08302c12021-06-09 10:08:27 +010060namespace arm_compute
61{
62namespace cpuinfo
63{
64namespace
65{
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010066#if !defined(_WIN64) && !defined(BARE_METAL) && !defined(__APPLE__) && !defined(__OpenBSD__) && \
67 (defined(__arm__) || defined(__aarch64__))
Georgios Pinitas08302c12021-06-09 10:08:27 +010068/** Extract MIDR using CPUID information that are exposed to user-space
69 *
70 * @param[in] max_num_cpus Maximum number of possible CPUs
71 *
72 * @return std::vector<uint32_t> A list of the MIDR of each core
73 */
74std::vector<uint32_t> midr_from_cpuid(uint32_t max_num_cpus)
75{
76 std::vector<uint32_t> cpus;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010077 for (unsigned int i = 0; i < max_num_cpus; ++i)
Georgios Pinitas08302c12021-06-09 10:08:27 +010078 {
79 std::stringstream str;
80 str << "/sys/devices/system/cpu/cpu" << i << "/regs/identification/midr_el1";
81 std::ifstream file(str.str(), std::ios::in);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010082 if (file.is_open())
Georgios Pinitas08302c12021-06-09 10:08:27 +010083 {
84 std::string line;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010085 if (bool(getline(file, line)))
Georgios Pinitas08302c12021-06-09 10:08:27 +010086 {
87 cpus.emplace_back(support::cpp11::stoul(line, nullptr, support::cpp11::NumericBase::BASE_16));
88 }
89 }
90 }
91 return cpus;
92}
93
94/** Extract MIDR by parsing the /proc/cpuinfo meta-data
95 *
96 * @param[in] max_num_cpus Maximum number of possible CPUs
97 *
98 * @return std::vector<uint32_t> A list of the MIDR of each core
99 */
100std::vector<uint32_t> midr_from_proc_cpuinfo(int max_num_cpus)
101{
102 std::vector<uint32_t> cpus;
103
104 regex_t proc_regex;
105 regex_t imp_regex;
106 regex_t var_regex;
107 regex_t part_regex;
108 regex_t rev_regex;
109
110 memset(&proc_regex, 0, sizeof(regex_t));
111 memset(&imp_regex, 0, sizeof(regex_t));
112 memset(&var_regex, 0, sizeof(regex_t));
113 memset(&part_regex, 0, sizeof(regex_t));
114 memset(&rev_regex, 0, sizeof(regex_t));
115
116 int ret_status = 0;
117 // If "long-form" cpuinfo is present, parse that to populate models.
118 ret_status |= regcomp(&proc_regex, R"(^processor.*([[:digit:]]+)$)", REG_EXTENDED);
119 ret_status |= regcomp(&imp_regex, R"(^CPU implementer.*0x(..)$)", REG_EXTENDED);
120 ret_status |= regcomp(&var_regex, R"(^CPU variant.*0x(.)$)", REG_EXTENDED);
121 ret_status |= regcomp(&part_regex, R"(^CPU part.*0x(...)$)", REG_EXTENDED);
122 ret_status |= regcomp(&rev_regex, R"(^CPU revision.*([[:digit:]]+)$)", REG_EXTENDED);
123 ARM_COMPUTE_UNUSED(ret_status);
124 ARM_COMPUTE_ERROR_ON_MSG(ret_status != 0, "Regex compilation failed.");
125
126 std::ifstream file("/proc/cpuinfo", std::ios::in);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100127 if (file.is_open())
Georgios Pinitas08302c12021-06-09 10:08:27 +0100128 {
129 std::string line;
130 int midr = 0;
131 int curcpu = -1;
132
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100133 while (bool(getline(file, line)))
Georgios Pinitas08302c12021-06-09 10:08:27 +0100134 {
135 std::array<regmatch_t, 2> match;
136 ret_status = regexec(&proc_regex, line.c_str(), 2, match.data(), 0);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100137 if (ret_status == 0)
Georgios Pinitas08302c12021-06-09 10:08:27 +0100138 {
139 std::string id = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
140 int newcpu = support::cpp11::stoi(id, nullptr);
141
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100142 if (curcpu >= 0 && midr == 0)
Georgios Pinitas08302c12021-06-09 10:08:27 +0100143 {
144 // Matched a new CPU ID without any description of the previous one - looks like old format.
145 return {};
146 }
147
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100148 if (curcpu >= 0 && curcpu < max_num_cpus)
Georgios Pinitas08302c12021-06-09 10:08:27 +0100149 {
150 cpus.emplace_back(midr);
151 }
152 else
153 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100154 ARM_COMPUTE_LOG_INFO_MSG_CORE(
155 "Trying to populate a core id with id greater than the expected number of cores!");
Georgios Pinitas08302c12021-06-09 10:08:27 +0100156 }
157
158 midr = 0;
159 curcpu = newcpu;
160
161 continue;
162 }
163
164 ret_status = regexec(&imp_regex, line.c_str(), 2, match.data(), 0);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100165 if (ret_status == 0)
Georgios Pinitas08302c12021-06-09 10:08:27 +0100166 {
167 std::string subexp = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
168 int impv = support::cpp11::stoi(subexp, nullptr, support::cpp11::NumericBase::BASE_16);
169 midr |= (impv << 24);
170
171 continue;
172 }
173
174 ret_status = regexec(&var_regex, line.c_str(), 2, match.data(), 0);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100175 if (ret_status == 0)
Georgios Pinitas08302c12021-06-09 10:08:27 +0100176 {
177 std::string subexp = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
178 int varv = support::cpp11::stoi(subexp, nullptr, support::cpp11::NumericBase::BASE_16);
179 midr |= (varv << 20);
180
181 continue;
182 }
183
184 ret_status = regexec(&part_regex, line.c_str(), 2, match.data(), 0);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100185 if (ret_status == 0)
Georgios Pinitas08302c12021-06-09 10:08:27 +0100186 {
187 std::string subexp = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
188 int partv = support::cpp11::stoi(subexp, nullptr, support::cpp11::NumericBase::BASE_16);
189 midr |= (partv << 4);
190
191 continue;
192 }
193
194 ret_status = regexec(&rev_regex, line.c_str(), 2, match.data(), 0);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100195 if (ret_status == 0)
Georgios Pinitas08302c12021-06-09 10:08:27 +0100196 {
197 std::string subexp = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
198 int regv = support::cpp11::stoi(subexp, nullptr);
199 midr |= (regv);
200 midr |= (0xf << 16);
201
202 continue;
203 }
204 }
205
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100206 if (curcpu >= 0 && curcpu < max_num_cpus)
Georgios Pinitas08302c12021-06-09 10:08:27 +0100207 {
208 cpus.emplace_back(midr);
209 }
210 else
211 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100212 ARM_COMPUTE_LOG_INFO_MSG_CORE(
213 "Trying to populate a core id with id greater than the expected number of cores!");
Georgios Pinitas08302c12021-06-09 10:08:27 +0100214 }
215 }
216
217 // Free allocated memory
218 regfree(&proc_regex);
219 regfree(&imp_regex);
220 regfree(&var_regex);
221 regfree(&part_regex);
222 regfree(&rev_regex);
223
224 return cpus;
225}
226
227/** Get the maximim number of CPUs in the system by parsing /sys/devices/system/cpu/present
228 *
229 * @return int Maximum number of CPUs
230 */
231int get_max_cpus()
232{
233 int max_cpus = 1;
234 std::ifstream CPUspresent;
235 CPUspresent.open("/sys/devices/system/cpu/present", std::ios::in);
236 bool success = false;
237
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100238 if (CPUspresent.is_open())
Georgios Pinitas08302c12021-06-09 10:08:27 +0100239 {
240 std::string line;
241
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100242 if (bool(getline(CPUspresent, line)))
Georgios Pinitas08302c12021-06-09 10:08:27 +0100243 {
244 /* The content of this file is a list of ranges or single values, e.g.
245 * 0-5, or 1-3,5,7 or similar. As we are interested in the
246 * max valid ID, we just need to find the last valid
247 * delimiter ('-' or ',') and parse the integer immediately after that.
248 */
249 auto startfrom = line.begin();
250
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100251 for (auto i = line.begin(); i < line.end(); ++i)
Georgios Pinitas08302c12021-06-09 10:08:27 +0100252 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100253 if (*i == '-' || *i == ',')
Georgios Pinitas08302c12021-06-09 10:08:27 +0100254 {
255 startfrom = i + 1;
256 }
257 }
258
259 line.erase(line.begin(), startfrom);
260
261 max_cpus = support::cpp11::stoi(line, nullptr) + 1;
262 success = true;
263 }
264 }
265
266 // Return std::thread::hardware_concurrency() as a fallback.
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100267 if (!success)
Georgios Pinitas08302c12021-06-09 10:08:27 +0100268 {
269 max_cpus = std::thread::hardware_concurrency();
270 }
271 return max_cpus;
272}
Omar Al Khatibf5053f72024-05-09 16:06:23 +0100273
274const static std::map<std::string, std::vector<uint32_t>> known_configurations_with_little_cores = {
275 {"xiaomi14-pro", {379, 379, 923, 923, 923, 867, 867, 1024}}};
276
277const static std::map<std::string, uint32_t> number_of_cores_to_use = {{"xiaomi14-pro", 6}};
278
279#if defined(__ANDROID__)
280std::vector<uint32_t> get_cpu_capacities()
281{
282 std::vector<uint32_t> cpu_capacities;
283 for (int i = 0; i < get_max_cpus(); ++i)
284 {
285 std::stringstream str;
286 str << "/sys/devices/system/cpu/cpu" << i << "/cpu_capacity";
287 std::ifstream file(str.str(), std::ios::in);
288 if (file.is_open())
289 {
290 std::string line;
291 if (bool(getline(file, line)))
292 {
293 cpu_capacities.emplace_back(support::cpp11::stoul(line));
294 }
295 }
296 }
297
298 return cpu_capacities;
299}
300
301uint32_t not_little_num_cpus_internal()
302{
303 std::vector<uint32_t> cpus_all = get_cpu_capacities();
304 std::vector<uint32_t> cpus_not_little;
305
306 for (auto &it : known_configurations_with_little_cores)
307 {
308 if (it.second == cpus_all)
309 {
310 return number_of_cores_to_use.find(it.first)->second;
311 }
312 }
313
314 std::vector<uint32_t>::iterator result = std::max_element(cpus_all.begin(), cpus_all.end());
315 uint32_t max_capacity = *result;
316 uint32_t threshold = max_capacity / 2;
317 for (unsigned int i = 0; i < cpus_all.size(); i++)
318 {
319 if (!(cpus_all[i] < threshold))
320 {
321 cpus_not_little.emplace_back(cpus_all[i]);
322 }
323 }
324 return cpus_not_little.size();
325}
326
327bool has_little_mid_big_internal()
328{
329 std::vector<uint32_t> cpus_all = get_cpu_capacities();
330 std::vector<uint32_t> cpus_not_little;
331
332 for (auto &it : known_configurations_with_little_cores)
333 {
334 if (it.second == cpus_all)
335 {
336 return true;
337 }
338 }
339 std::sort(cpus_all.begin(), cpus_all.end());
340 std::vector<uint32_t>::iterator ip;
341 ip = std::unique(cpus_all.begin(), cpus_all.end());
342 cpus_all.resize(std::distance(cpus_all.begin(), ip));
343
344 if (cpus_all.size() == 3)
345 {
346 return true;
347 }
348 else
349 {
350 return false;
351 }
352}
353#endif /* defined(__ANDROID__) */
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100354#elif defined(__aarch64__) && \
355 defined(__APPLE__) /* !defined(BARE_METAL) && !defined(__APPLE__) && (defined(__arm__) || defined(__aarch64__)) */
Pablo Marquez Tello639f0f62022-01-21 15:25:27 +0000356/** Query features through sysctlbyname
357 *
358 * @return int value queried
359 */
Pablo Tello4e66d702022-03-07 18:20:12 +0000360int get_hw_capability(const std::string &cap)
Pablo Marquez Tello639f0f62022-01-21 15:25:27 +0000361{
Pablo Tello4e66d702022-03-07 18:20:12 +0000362 int64_t result(0);
363 size_t size = sizeof(result);
364 sysctlbyname(cap.c_str(), &result, &size, NULL, 0);
365 return result;
Pablo Marquez Tello639f0f62022-01-21 15:25:27 +0000366}
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100367#endif /* !defined(BARE_METAL) && !defined(__APPLE__) && !defined(__OpenBSD__) && (defined(__arm__) || defined(__aarch64__)) */
Michalis Spyrou20fca522021-06-07 14:23:57 +0100368
369#if defined(BARE_METAL) && defined(__aarch64__)
370uint64_t get_sve_feature_reg()
371{
372 uint64_t svefr0 = 0;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100373 __asm __volatile(".inst 0xd5380483 // mrs x3, ID_AA64ZFR0_EL1\n"
374 "MOV %0, X3"
375 : "=r"(svefr0)
376 :
377 : "x3");
Michalis Spyrou20fca522021-06-07 14:23:57 +0100378 return svefr0;
379}
380#endif /* defined(BARE_METAL) && defined(__aarch64__) */
Georgios Pinitas08302c12021-06-09 10:08:27 +0100381} // namespace
382
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100383CpuInfo::CpuInfo(CpuIsaInfo isa, std::vector<CpuModel> cpus) : _isa(std::move(isa)), _cpus(std::move(cpus))
Georgios Pinitas08302c12021-06-09 10:08:27 +0100384{
385}
386
387CpuInfo CpuInfo::build()
388{
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100389#if !defined(_WIN64) && !defined(BARE_METAL) && !defined(__APPLE__) && !defined(__OpenBSD__) && \
390 (defined(__arm__) || defined(__aarch64__))
Georgios Pinitas08302c12021-06-09 10:08:27 +0100391 const uint32_t hwcaps = getauxval(AT_HWCAP);
392 const uint32_t hwcaps2 = getauxval(AT_HWCAP2);
393 const uint32_t max_cpus = get_max_cpus();
394
395 // Populate midr values
396 std::vector<uint32_t> cpus_midr;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100397 if (hwcaps & ARM_COMPUTE_CPU_FEATURE_HWCAP_CPUID)
Georgios Pinitas08302c12021-06-09 10:08:27 +0100398 {
399 cpus_midr = midr_from_cpuid(max_cpus);
400 }
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100401 if (cpus_midr.empty())
Georgios Pinitas08302c12021-06-09 10:08:27 +0100402 {
403 cpus_midr = midr_from_proc_cpuinfo(max_cpus);
404 }
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100405 if (cpus_midr.empty())
Georgios Pinitas08302c12021-06-09 10:08:27 +0100406 {
407 cpus_midr.resize(max_cpus, 0);
408 }
409
410 // Populate isa (Assume homogeneous ISA specification)
411 CpuIsaInfo isa = init_cpu_isa_from_hwcaps(hwcaps, hwcaps2, cpus_midr.back());
412
413 // Convert midr to models
414 std::vector<CpuModel> cpus_model;
415 std::transform(std::begin(cpus_midr), std::end(cpus_midr), std::back_inserter(cpus_model),
416 [](uint32_t midr) -> CpuModel { return midr_to_model(midr); });
417
418 CpuInfo info(isa, cpus_model);
419 return info;
420
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100421#elif (BARE_METAL) && \
422 defined( \
423 __aarch64__) /* !defined(BARE_METAL) && !defined(__APPLE__) && !defined(__OpenBSD__) && (defined(__arm__) || defined(__aarch64__)) */
Georgios Pinitas08302c12021-06-09 10:08:27 +0100424
425 // Assume single CPU in bare metal mode. Just read the ID register and feature bits directly.
Viet-Hoa Do03b29712022-06-01 11:47:14 +0100426 uint64_t isar0 = 0, isar1 = 0, pfr0 = 0, pfr1 = 0, svefr0 = 0, midr = 0;
Georgios Pinitas08302c12021-06-09 10:08:27 +0100427 ARM_COMPUTE_GET_FEATURE_REG(isar0, ID_AA64ISAR0_EL1);
428 ARM_COMPUTE_GET_FEATURE_REG(isar1, ID_AA64ISAR1_EL1);
429 ARM_COMPUTE_GET_FEATURE_REG(pfr0, ID_AA64PFR0_EL1);
Viet-Hoa Do03b29712022-06-01 11:47:14 +0100430 ARM_COMPUTE_GET_FEATURE_REG(pfr1, ID_AA64PFR1_EL1);
Georgios Pinitas08302c12021-06-09 10:08:27 +0100431 ARM_COMPUTE_GET_FEATURE_REG(midr, MIDR_EL1);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100432 if ((pfr0 >> 32) & 0xf)
Georgios Pinitas08302c12021-06-09 10:08:27 +0100433 {
434 svefr0 = get_sve_feature_reg();
435 }
436
Viet-Hoa Do03b29712022-06-01 11:47:14 +0100437 CpuIsaInfo isa = init_cpu_isa_from_regs(isar0, isar1, pfr0, pfr1, svefr0, midr);
Georgios Pinitas08302c12021-06-09 10:08:27 +0100438 std::vector<CpuModel> cpus_model(1, midr_to_model(midr));
439 CpuInfo info(isa, cpus_model);
440 return info;
Pablo Marquez Tello639f0f62022-01-21 15:25:27 +0000441#elif defined(__aarch64__) && defined(__APPLE__) /* #elif(BARE_METAL) && defined(__aarch64__) */
Pablo Marquez Tello67c3c632023-01-23 17:00:41 +0000442 int ncpus = get_hw_capability("hw.perflevel0.logicalcpu");
Pablo Tello4e66d702022-03-07 18:20:12 +0000443 CpuIsaInfo isainfo;
Pablo Marquez Tello639f0f62022-01-21 15:25:27 +0000444 std::vector<CpuModel> cpus_model(ncpus);
445 isainfo.neon = get_hw_capability("hw.optional.neon");
446 isainfo.fp16 = get_hw_capability("hw.optional.neon_fp16");
Pablo Tello4e66d702022-03-07 18:20:12 +0000447 isainfo.dot = get_hw_capability("hw.optional.arm.FEAT_DotProd");
Viet-Hoa Do4c3f7162024-05-10 15:21:01 +0100448 isainfo.bf16 = get_hw_capability("hw.optional.arm.FEAT_BF16");
449 isainfo.i8mm = get_hw_capability("hw.optional.arm.FEAT_I8MM");
Pablo Tello4e66d702022-03-07 18:20:12 +0000450 CpuInfo info(isainfo, cpus_model);
Pablo Marquez Tello639f0f62022-01-21 15:25:27 +0000451 return info;
Pablo Marquez Tello6fe9eaf2024-02-29 16:36:09 +0000452#elif defined(__aarch64__) && defined(_WIN64) /* #elif defined(__aarch64__) && defined(__APPLE__) */
453 CpuIsaInfo isainfo;
454 isainfo.neon = true;
455 CpuInfo info(isainfo, {CpuModel::GENERIC});
456 return info;
457#else /* #elif defined(__aarch64__) && defined(_WIN64) */
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100458 CpuInfo info(CpuIsaInfo(), {CpuModel::GENERIC});
Georgios Pinitas08302c12021-06-09 10:08:27 +0100459 return info;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100460#endif /* !defined(BARE_METAL) && !defined(__APPLE__) && !defined(__OpenBSD__) && (defined(__arm__) || defined(__aarch64__)) */
Georgios Pinitas08302c12021-06-09 10:08:27 +0100461}
462
463CpuModel CpuInfo::cpu_model(uint32_t cpuid) const
464{
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100465 if (cpuid < _cpus.size())
Georgios Pinitas08302c12021-06-09 10:08:27 +0100466 {
467 return _cpus[cpuid];
468 }
469 return CpuModel::GENERIC;
470}
471
472CpuModel CpuInfo::cpu_model() const
473{
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100474#if defined(_WIN64) || defined(BARE_METAL) || defined(__APPLE__) || defined(__OpenBSD__) || \
475 (!defined(__arm__) && !defined(__aarch64__))
Georgios Pinitas08302c12021-06-09 10:08:27 +0100476 return cpu_model(0);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100477#else /* defined(BARE_METAL) || defined(__APPLE__) || defined(__OpenBSD__) || (!defined(__arm__) && !defined(__aarch64__)) */
Georgios Pinitas08302c12021-06-09 10:08:27 +0100478 return cpu_model(sched_getcpu());
Kevin Lo7195f712022-01-07 15:46:02 +0800479#endif /* defined(BARE_METAL) || defined(__APPLE__) || defined(__OpenBSD__) || (!defined(__arm__) && !defined(__aarch64__)) */
Georgios Pinitas08302c12021-06-09 10:08:27 +0100480}
481
482uint32_t CpuInfo::num_cpus() const
483{
484 return _cpus.size();
485}
486
Omar Al Khatibf5053f72024-05-09 16:06:23 +0100487uint32_t CpuInfo::not_little_num_cpus() const
488{
489#if defined(__ANDROID__)
490 return not_little_num_cpus_internal();
491#else /* defined(__ANDROID__) */
492 return num_cpus();
493#endif /* defined(__ANDROID__) */
494}
495
496bool CpuInfo::has_little_mid_big() const
497{
498#if defined(__ANDROID__)
499 return has_little_mid_big_internal();
500#else /* defined(__ANDROID__) */
501 return false;
502#endif /* defined(__ANDROID__) */
503}
504
Georgios Pinitas08302c12021-06-09 10:08:27 +0100505uint32_t num_threads_hint()
506{
507 unsigned int num_threads_hint = 1;
508
Viet-Hoa Do13321f72023-02-24 17:32:27 +0000509#if !defined(BARE_METAL) && !defined(_WIN64) && !defined(ARM_COMPUTE_DISABLE_THREADS_HINT)
Georgios Pinitas08302c12021-06-09 10:08:27 +0100510 std::vector<std::string> cpus;
511 cpus.reserve(64);
512
513 // CPU part regex
514 regex_t cpu_part_rgx;
515 memset(&cpu_part_rgx, 0, sizeof(regex_t));
516 int ret_status = regcomp(&cpu_part_rgx, R"(.*CPU part.+/?\:[[:space:]]+([[:alnum:]]+).*)", REG_EXTENDED);
517 ARM_COMPUTE_UNUSED(ret_status);
518 ARM_COMPUTE_ERROR_ON_MSG(ret_status != 0, "Regex compilation failed.");
519
520 // Read cpuinfo and get occurrence of each core
521 std::ifstream cpuinfo_file("/proc/cpuinfo", std::ios::in);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100522 if (cpuinfo_file.is_open())
Georgios Pinitas08302c12021-06-09 10:08:27 +0100523 {
524 std::string line;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100525 while (bool(getline(cpuinfo_file, line)))
Georgios Pinitas08302c12021-06-09 10:08:27 +0100526 {
527 std::array<regmatch_t, 2> match;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100528 if (regexec(&cpu_part_rgx, line.c_str(), 2, match.data(), 0) == 0)
Georgios Pinitas08302c12021-06-09 10:08:27 +0100529 {
530 cpus.emplace_back(line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so)));
531 }
532 }
533 }
534 regfree(&cpu_part_rgx);
535
536 // Get min number of threads
537 std::sort(std::begin(cpus), std::end(cpus));
538 auto least_frequent_cpu_occurences = [](const std::vector<std::string> &cpus) -> uint32_t
539 {
540 std::unordered_map<std::string, uint32_t> cpus_freq;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100541 for (const auto &cpu : cpus)
Georgios Pinitas08302c12021-06-09 10:08:27 +0100542 {
543 cpus_freq[cpu]++;
544 }
545
546 uint32_t vmin = cpus.size() + 1;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100547 for (const auto &cpu_freq : cpus_freq)
Georgios Pinitas08302c12021-06-09 10:08:27 +0100548 {
549 vmin = std::min(vmin, cpu_freq.second);
550 }
551 return vmin;
552 };
553
554 // Set thread hint
555 num_threads_hint = cpus.empty() ? std::thread::hardware_concurrency() : least_frequent_cpu_occurences(cpus);
Viet-Hoa Do13321f72023-02-24 17:32:27 +0000556#endif /* !defined(BARE_METAL) && !defined(_WIN64) && !defined(ARM_COMPUTE_DISABLE_THREADS_HINT) */
Georgios Pinitas08302c12021-06-09 10:08:27 +0100557
558 return num_threads_hint;
559}
560} // namespace cpuinfo
Pablo Marquez Tello639f0f62022-01-21 15:25:27 +0000561} // namespace arm_compute