aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorgios Pinitas <georgios.pinitas@arm.com>2021-06-09 10:08:27 +0100
committerGeorgios Pinitas <georgios.pinitas@arm.com>2021-06-15 17:57:39 +0000
commit08302c17cd57356b35d46e17dc8d8f76672da5cf (patch)
tree3fed9bea3586bc140cc953e935f6ed55e8692dac
parent450dfb1b4d719d60295bfae56f4c46dcaf044d72 (diff)
downloadComputeLibrary-08302c17cd57356b35d46e17dc8d8f76672da5cf.tar.gz
Add CPU discovery capabilities.
Resolves: COMPMID-4500 Signed-off-by: Georgios Pinitas <georgios.pinitas@arm.com> Change-Id: I008c51934ef813fb1f489b531288c4419e701955 Reviewed-on: https://review.mlplatform.org/c/ml/ComputeLibrary/+/5799 Reviewed-by: Michele Di Giorgio <michele.digiorgio@arm.com> Tested-by: Arm Jenkins <bsgcomp@arm.com> Comments-Addressed: Arm Jenkins <bsgcomp@arm.com>
-rw-r--r--Android.bp7
-rw-r--r--arm_compute/core/CPP/CPPTypes.h169
-rw-r--r--arm_compute/runtime/IRuntimeContext.h8
-rw-r--r--arm_compute/runtime/IScheduler.h9
-rw-r--r--arm_compute/runtime/RuntimeContext.h9
-rw-r--r--filelist.json4
-rw-r--r--src/common/cpuinfo/CpuInfo.cpp405
-rw-r--r--src/common/cpuinfo/CpuInfo.h113
-rw-r--r--src/common/cpuinfo/CpuIsaInfo.cpp198
-rw-r--r--src/common/cpuinfo/CpuIsaInfo.h78
-rw-r--r--src/common/cpuinfo/CpuModel.cpp190
-rw-r--r--src/common/cpuinfo/CpuModel.h79
-rw-r--r--src/common/cpuinfo/target/CpuInfoSveUtils.cpp (renamed from src/runtime/DeviceProperties.cpp)18
-rw-r--r--src/common/cpuinfo/target/CpuInfoSveUtils.h (renamed from arm_compute/runtime/DeviceProperties.h)25
-rw-r--r--src/core/CPP/CPPTypes.cpp92
-rw-r--r--src/core/cpu/kernels/assembly/arm_gemm.hpp1
-rw-r--r--src/cpu/CpuContext.cpp2
-rw-r--r--src/runtime/CPP/CPPScheduler.cpp1
-rw-r--r--src/runtime/CPUUtils.cpp556
-rw-r--r--src/runtime/CPUUtils.h51
-rw-r--r--src/runtime/IScheduler.cpp5
-rw-r--r--src/runtime/MEMUtils.cpp103
-rw-r--r--src/runtime/OMP/OMPScheduler.cpp1
-rw-r--r--src/runtime/RuntimeContext.cpp9
-rw-r--r--tests/main.cpp5
-rw-r--r--utils/TypePrinter.h55
26 files changed, 1167 insertions, 1026 deletions
diff --git a/Android.bp b/Android.bp
index 45bc1be21b..87bdcfcccd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -62,6 +62,10 @@ cc_library_static {
"src/common/AllocatorWrapper.cpp",
"src/common/ITensorV2.cpp",
"src/common/TensorPack.cpp",
+ "src/common/cpuinfo/CpuInfo.cpp",
+ "src/common/cpuinfo/CpuIsaInfo.cpp",
+ "src/common/cpuinfo/CpuModel.cpp",
+ "src/common/cpuinfo/target/CpuInfoSveUtils.cpp",
"src/common/utils/LegacySupport.cpp",
"src/core/AccessWindowAutoPadding.cpp",
"src/core/AccessWindowStatic.cpp",
@@ -526,13 +530,10 @@ cc_library_static {
"src/runtime/CPP/functions/CPPPermute.cpp",
"src/runtime/CPP/functions/CPPTopKV.cpp",
"src/runtime/CPP/functions/CPPUpsample.cpp",
- "src/runtime/CPUUtils.cpp",
- "src/runtime/DeviceProperties.cpp",
"src/runtime/IScheduler.cpp",
"src/runtime/ISimpleLifetimeManager.cpp",
"src/runtime/ITensorAllocator.cpp",
"src/runtime/IWeightsManager.cpp",
- "src/runtime/MEMUtils.cpp",
"src/runtime/Memory.cpp",
"src/runtime/MemoryManagerOnDemand.cpp",
"src/runtime/NEON/INEOperator.cpp",
diff --git a/arm_compute/core/CPP/CPPTypes.h b/arm_compute/core/CPP/CPPTypes.h
index bc4464e7c9..11891937d1 100644
--- a/arm_compute/core/CPP/CPPTypes.h
+++ b/arm_compute/core/CPP/CPPTypes.h
@@ -26,103 +26,40 @@
#include "arm_compute/core/Error.h"
-#include <array>
-#include <string>
-#include <vector>
+#include <memory>
namespace arm_compute
{
-/** CPU models - we only need to detect CPUs we have
- * microarchitecture-specific code for.
+#define ARM_COMPUTE_CPU_MODEL_LIST \
+ X(GENERIC) \
+ X(GENERIC_FP16) \
+ X(GENERIC_FP16_DOT) \
+ X(A35) \
+ X(A53) \
+ X(A55r0) \
+ X(A55r1) \
+ X(A73) \
+ X(KLEIN) \
+ X(X1)
+
+/** CPU models types
*
- * Architecture features are detected via HWCAPs.
+ * @note We only need to detect CPUs we have microarchitecture-specific code for.
+ * @note Architecture features are detected via HWCAPs.
*/
enum class CPUModel
{
- GENERIC,
- GENERIC_FP16,
- GENERIC_FP16_DOT,
- A35,
- A53,
- A55r0,
- A55r1,
- KLEIN,
- X1,
- A73
+#define X(model) model,
+ ARM_COMPUTE_CPU_MODEL_LIST
+#undef X
};
-/** Global memory policy.
- * The functions in the runtime will use different strategies based on the policy currently set.
- *
- * MINIMIZE will try to reduce the amount allocated by the functions at the expense of performance normally.
- * NORMAL won't try to save any memory and will favor speed over memory consumption
- *
- */
-enum class MemoryPolicy
-{
- MINIMIZE,
- NORMAL
-};
-
-/** Convert a cpumodel value to a string
- *
- * @param val CPUModel value to be converted
- *
- * @return String representing the corresponding CPUModel.
- */
-inline std::string cpu_model_to_string(CPUModel val)
-{
- switch(val)
- {
- case CPUModel::GENERIC:
- {
- return std::string("GENERIC");
- }
- case CPUModel::KLEIN:
- {
- return std::string("KLEIN");
- }
- case CPUModel::GENERIC_FP16:
- {
- return std::string("GENERIC_FP16");
- }
- case CPUModel::GENERIC_FP16_DOT:
- {
- return std::string("GENERIC_FP16_DOT");
- }
- case CPUModel::A53:
- {
- return std::string("A53");
- }
- case CPUModel::A55r0:
- {
- return std::string("A55r0");
- }
- case CPUModel::A55r1:
- {
- return std::string("A55r1");
- }
- case CPUModel::X1:
- {
- return std::string("X1");
- }
- case CPUModel::A73:
- {
- return std::string("A73");
- }
- default:
- {
- ARM_COMPUTE_ERROR("Invalid CPUModel.");
- return std::string("GENERIC");
- }
- }
-}
-
class CPUInfo final
{
public:
/** Constructor */
CPUInfo();
+ ~CPUInfo();
/** Disable copy constructor and assignment operator to avoid copying the vector of CPUs each time
* CPUInfo is initialized once in the IScheduler and ThreadInfo will get a pointer to it.
@@ -137,6 +74,11 @@ public:
* @return true of the cpu supports fp16, false otherwise
*/
bool has_fp16() const;
+ /** Checks if the cpu model supports bf16.
+ *
+ * @return true of the cpu supports bf16, false otherwise
+ */
+ bool has_bf16() const;
/** Checks if the cpu model supports dot product.
*
* @return true of the cpu supports dot product, false otherwise
@@ -169,16 +111,6 @@ public:
* @return the size of the L1 cache
*/
unsigned int get_L2_cache_size() const;
- /** Set the L1 cache size
- *
- * @param[in] size the new size to be set.
- */
- void set_L1_cache_size(unsigned int size);
- /** Set the L2 cache size
- *
- * @param[in] size the new size to be set.
- */
- void set_L2_cache_size(unsigned int size);
/** Set fp16 support
*
* @param[in] fp16 whether the cpu supports fp16.
@@ -189,22 +121,6 @@ public:
* @param[in] dotprod whether the cpu supports dot product.
*/
void set_dotprod(const bool dotprod);
- /** Set sve support
- *
- * @param[in] sve whether the cpu supports sve.
- */
- void set_sve(const bool sve);
- /** Set the cpumodel for a given cpu core
- *
- * @param[in] cpuid the id of the core to be set.
- * @param[in] model the @ref CPUModel to be set.
- */
- void set_cpu_model(unsigned int cpuid, CPUModel model);
- /** Set max number of cpus
- *
- * @param[in] cpu_count the number of CPUs in the system.
- */
- void set_cpu_num(unsigned int cpu_count);
/** Return the maximum number of CPUs present
*
@@ -213,39 +129,8 @@ public:
unsigned int get_cpu_num() const;
private:
- std::vector<CPUModel> _percpu = {};
- bool _fp16 = false;
- bool _dotprod = false;
- bool _sve = false;
- unsigned int _L1_cache_size = 32768;
- unsigned int _L2_cache_size = 262144;
-};
-
-class MEMInfo final
-{
-public:
- MEMInfo();
-
- /** Return the total amount of RAM memory in the system expressed in KB.
- *
- * @return Total memory
- */
- size_t get_total_in_kb() const;
-
- static void set_policy(MemoryPolicy policy);
- static MemoryPolicy get_policy();
-
- /** Common memory sizes expressed in Kb to avoid having them
- * duplicated throughout the code.
- */
- static const size_t ONE_GB_IN_KB = { 1035842 };
- static const size_t TWO_GB_IN_KB = { ONE_GB_IN_KB * 2 };
-
-private:
- size_t _total;
- size_t _free;
- size_t _buffer;
- static MemoryPolicy _policy;
+ struct Impl;
+ std::unique_ptr<Impl> _impl;
};
/** Information about executing thread and CPU. */
diff --git a/arm_compute/runtime/IRuntimeContext.h b/arm_compute/runtime/IRuntimeContext.h
index 4751fc2ddc..8e61cfb8a8 100644
--- a/arm_compute/runtime/IRuntimeContext.h
+++ b/arm_compute/runtime/IRuntimeContext.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 Arm Limited.
+ * Copyright (c) 2019, 2021 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -29,7 +29,6 @@ namespace arm_compute
// Forward declarations
class IScheduler;
class IAssetManager;
-struct DeviceProperties;
/** Context interface */
class IRuntimeContext
@@ -51,11 +50,6 @@ public:
* @return The asset manager registered to the context
*/
virtual IAssetManager *asset_manager() = 0;
- /** Device propertied accessor
- *
- * @return Device properties
- */
- virtual const DeviceProperties &properties() = 0;
};
} // namespace arm_compute
#endif /*ARM_COMPUTE_IRUNTIME_CONTEXT_H */
diff --git a/arm_compute/runtime/IScheduler.h b/arm_compute/runtime/IScheduler.h
index d3ba86a67b..417c62cc9c 100644
--- a/arm_compute/runtime/IScheduler.h
+++ b/arm_compute/runtime/IScheduler.h
@@ -127,9 +127,9 @@ public:
}
private:
- unsigned int _split_dimension;
- StrategyHint _strategy;
- int _threshold;
+ unsigned int _split_dimension{};
+ StrategyHint _strategy{};
+ int _threshold{};
};
/** Signature for the workloads to execute */
using Workload = std::function<void(const ThreadInfo &)>;
@@ -205,7 +205,6 @@ protected:
* @param[in] workloads Array of workloads to run
*/
virtual void run_workloads(std::vector<Workload> &workloads) = 0;
- CPUInfo _cpu_info;
/** Common scheduler logic to execute the given kernel
*
@@ -216,6 +215,8 @@ protected:
*/
void schedule_common(ICPPKernel *kernel, const Hints &hints, const Window &window, ITensorPack &tensors);
+ CPUInfo _cpu_info{};
+
private:
unsigned int _num_threads_hint = {};
};
diff --git a/arm_compute/runtime/RuntimeContext.h b/arm_compute/runtime/RuntimeContext.h
index 31e2d6939a..23bd267375 100644
--- a/arm_compute/runtime/RuntimeContext.h
+++ b/arm_compute/runtime/RuntimeContext.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 Arm Limited.
+ * Copyright (c) 2019, 2021 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -24,7 +24,6 @@
#ifndef ARM_COMPUTE_RUNTIME_CONTEXT_H
#define ARM_COMPUTE_RUNTIME_CONTEXT_H
-#include "arm_compute/runtime/DeviceProperties.h"
#include "arm_compute/runtime/IRuntimeContext.h"
#include <memory>
@@ -51,14 +50,12 @@ public:
void set_scheduler(IScheduler *scheduler);
// Inherited overridden methods
- IScheduler *scheduler() override;
- IAssetManager *asset_manager() override;
- const DeviceProperties &properties() override;
+ IScheduler *scheduler() override;
+ IAssetManager *asset_manager() override;
private:
std::unique_ptr<IScheduler> _owned_scheduler{ nullptr };
IScheduler *_scheduler{ nullptr };
- DeviceProperties _device_props{};
};
} // namespace arm_compute
#endif /*ARM_COMPUTE_RUNTIME_CONTEXT_H */
diff --git a/filelist.json b/filelist.json
index c8b4574c21..c375b1daef 100644
--- a/filelist.json
+++ b/filelist.json
@@ -1,5 +1,9 @@
{
"common" : [
+ "src/common/cpuinfo/target/CpuInfoSveUtils.cpp",
+ "src/common/cpuinfo/CpuInfo.cpp",
+ "src/common/cpuinfo/CpuModel.cpp",
+ "src/common/cpuinfo/CpuIsaInfo.cpp",
"src/common/utils/LegacySupport.cpp",
"src/common/AllocatorWrapper.cpp",
"src/common/ITensorV2.cpp",
diff --git a/src/common/cpuinfo/CpuInfo.cpp b/src/common/cpuinfo/CpuInfo.cpp
new file mode 100644
index 0000000000..436e7ea803
--- /dev/null
+++ b/src/common/cpuinfo/CpuInfo.cpp
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 2021 Arm Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "src/common/cpuinfo/CpuInfo.h"
+
+#include "arm_compute/core/Error.h"
+#include "arm_compute/core/Log.h"
+#include "src/common/cpuinfo/target/CpuInfoSveUtils.h"
+#include "support/StringSupport.h"
+#include "support/ToolchainSupport.h"
+
+#include <sstream>
+
+#if !defined(BARE_METAL)
+#include <algorithm>
+#include <cstring>
+#include <fstream>
+#include <regex.h> /* C++ std::regex takes up a lot of space in the standalone builds */
+#include <sched.h>
+#include <thread>
+#include <unordered_map>
+#endif /* !defined(BARE_METAL) */
+
+#if !defined(BARE_METAL) && !defined(__APPLE__) && (defined(__arm__) || defined(__aarch64__))
+#include <asm/hwcap.h> /* Get HWCAP bits from asm/hwcap.h */
+#include <sys/auxv.h>
+#endif /* !defined(BARE_METAL) && !defined(__APPLE__) && (defined(__arm__) || defined(__aarch64__)) */
+
+#define ARM_COMPUTE_CPU_FEATURE_HWCAP_CPUID (1 << 11)
+#define ARM_COMPUTE_GET_FEATURE_REG(var, freg) __asm __volatile("MRS %0, " #freg \
+ : "=r"(var))
+namespace arm_compute
+{
+namespace cpuinfo
+{
+namespace
+{
+#if !defined(BARE_METAL) && !defined(__APPLE__) && (defined(__arm__) || defined(__aarch64__))
+/** Extract MIDR using CPUID information that are exposed to user-space
+ *
+ * @param[in] max_num_cpus Maximum number of possible CPUs
+ *
+ * @return std::vector<uint32_t> A list of the MIDR of each core
+ */
+std::vector<uint32_t> midr_from_cpuid(uint32_t max_num_cpus)
+{
+ std::vector<uint32_t> cpus;
+ for(unsigned int i = 0; i < max_num_cpus; ++i)
+ {
+ std::stringstream str;
+ str << "/sys/devices/system/cpu/cpu" << i << "/regs/identification/midr_el1";
+ std::ifstream file(str.str(), std::ios::in);
+ if(file.is_open())
+ {
+ std::string line;
+ if(bool(getline(file, line)))
+ {
+ cpus.emplace_back(support::cpp11::stoul(line, nullptr, support::cpp11::NumericBase::BASE_16));
+ }
+ }
+ }
+ return cpus;
+}
+
+/** Extract MIDR by parsing the /proc/cpuinfo meta-data
+ *
+ * @param[in] max_num_cpus Maximum number of possible CPUs
+ *
+ * @return std::vector<uint32_t> A list of the MIDR of each core
+ */
+std::vector<uint32_t> midr_from_proc_cpuinfo(int max_num_cpus)
+{
+ std::vector<uint32_t> cpus;
+
+ regex_t proc_regex;
+ regex_t imp_regex;
+ regex_t var_regex;
+ regex_t part_regex;
+ regex_t rev_regex;
+
+ memset(&proc_regex, 0, sizeof(regex_t));
+ memset(&imp_regex, 0, sizeof(regex_t));
+ memset(&var_regex, 0, sizeof(regex_t));
+ memset(&part_regex, 0, sizeof(regex_t));
+ memset(&rev_regex, 0, sizeof(regex_t));
+
+ int ret_status = 0;
+ // If "long-form" cpuinfo is present, parse that to populate models.
+ ret_status |= regcomp(&proc_regex, R"(^processor.*([[:digit:]]+)$)", REG_EXTENDED);
+ ret_status |= regcomp(&imp_regex, R"(^CPU implementer.*0x(..)$)", REG_EXTENDED);
+ ret_status |= regcomp(&var_regex, R"(^CPU variant.*0x(.)$)", REG_EXTENDED);
+ ret_status |= regcomp(&part_regex, R"(^CPU part.*0x(...)$)", REG_EXTENDED);
+ ret_status |= regcomp(&rev_regex, R"(^CPU revision.*([[:digit:]]+)$)", REG_EXTENDED);
+ ARM_COMPUTE_UNUSED(ret_status);
+ ARM_COMPUTE_ERROR_ON_MSG(ret_status != 0, "Regex compilation failed.");
+
+ std::ifstream file("/proc/cpuinfo", std::ios::in);
+ if(file.is_open())
+ {
+ std::string line;
+ int midr = 0;
+ int curcpu = -1;
+
+ while(bool(getline(file, line)))
+ {
+ std::array<regmatch_t, 2> match;
+ ret_status = regexec(&proc_regex, line.c_str(), 2, match.data(), 0);
+ if(ret_status == 0)
+ {
+ std::string id = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
+ int newcpu = support::cpp11::stoi(id, nullptr);
+
+ if(curcpu >= 0 && midr == 0)
+ {
+ // Matched a new CPU ID without any description of the previous one - looks like old format.
+ return {};
+ }
+
+ if(curcpu >= 0 && curcpu < max_num_cpus)
+ {
+ cpus.emplace_back(midr);
+ }
+ else
+ {
+ ARM_COMPUTE_LOG_INFO_MSG_CORE("Trying to populate a core id with id greater than the expected number of cores!");
+ }
+
+ midr = 0;
+ curcpu = newcpu;
+
+ continue;
+ }
+
+ ret_status = regexec(&imp_regex, line.c_str(), 2, match.data(), 0);
+ if(ret_status == 0)
+ {
+ std::string subexp = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
+ int impv = support::cpp11::stoi(subexp, nullptr, support::cpp11::NumericBase::BASE_16);
+ midr |= (impv << 24);
+
+ continue;
+ }
+
+ ret_status = regexec(&var_regex, line.c_str(), 2, match.data(), 0);
+ if(ret_status == 0)
+ {
+ std::string subexp = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
+ int varv = support::cpp11::stoi(subexp, nullptr, support::cpp11::NumericBase::BASE_16);
+ midr |= (varv << 20);
+
+ continue;
+ }
+
+ ret_status = regexec(&part_regex, line.c_str(), 2, match.data(), 0);
+ if(ret_status == 0)
+ {
+ std::string subexp = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
+ int partv = support::cpp11::stoi(subexp, nullptr, support::cpp11::NumericBase::BASE_16);
+ midr |= (partv << 4);
+
+ continue;
+ }
+
+ ret_status = regexec(&rev_regex, line.c_str(), 2, match.data(), 0);
+ if(ret_status == 0)
+ {
+ std::string subexp = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
+ int regv = support::cpp11::stoi(subexp, nullptr);
+ midr |= (regv);
+ midr |= (0xf << 16);
+
+ continue;
+ }
+ }
+
+ if(curcpu >= 0 && curcpu < max_num_cpus)
+ {
+ cpus.emplace_back(midr);
+ }
+ else
+ {
+ ARM_COMPUTE_LOG_INFO_MSG_CORE("Trying to populate a core id with id greater than the expected number of cores!");
+ }
+ }
+
+ // Free allocated memory
+ regfree(&proc_regex);
+ regfree(&imp_regex);
+ regfree(&var_regex);
+ regfree(&part_regex);
+ regfree(&rev_regex);
+
+ return cpus;
+}
+
+/** Get the maximim number of CPUs in the system by parsing /sys/devices/system/cpu/present
+ *
+ * @return int Maximum number of CPUs
+ */
+int get_max_cpus()
+{
+ int max_cpus = 1;
+ std::ifstream CPUspresent;
+ CPUspresent.open("/sys/devices/system/cpu/present", std::ios::in);
+ bool success = false;
+
+ if(CPUspresent.is_open())
+ {
+ std::string line;
+
+ if(bool(getline(CPUspresent, line)))
+ {
+ /* The content of this file is a list of ranges or single values, e.g.
+ * 0-5, or 1-3,5,7 or similar. As we are interested in the
+ * max valid ID, we just need to find the last valid
+ * delimiter ('-' or ',') and parse the integer immediately after that.
+ */
+ auto startfrom = line.begin();
+
+ for(auto i = line.begin(); i < line.end(); ++i)
+ {
+ if(*i == '-' || *i == ',')
+ {
+ startfrom = i + 1;
+ }
+ }
+
+ line.erase(line.begin(), startfrom);
+
+ max_cpus = support::cpp11::stoi(line, nullptr) + 1;
+ success = true;
+ }
+ }
+
+ // Return std::thread::hardware_concurrency() as a fallback.
+ if(!success)
+ {
+ max_cpus = std::thread::hardware_concurrency();
+ }
+ return max_cpus;
+}
+#endif /* !defined(BARE_METAL) && !defined(__APPLE__) && (defined(__arm__) || defined(__aarch64__)) */
+} // namespace
+
+CpuInfo::CpuInfo(CpuIsaInfo isa, std::vector<CpuModel> cpus)
+ : _isa(std::move(isa)), _cpus(std::move(cpus))
+{
+}
+
+CpuInfo CpuInfo::build()
+{
+#if !defined(BARE_METAL) && !defined(__APPLE__) && (defined(__arm__) || defined(__aarch64__))
+ const uint32_t hwcaps = getauxval(AT_HWCAP);
+ const uint32_t hwcaps2 = getauxval(AT_HWCAP2);
+ const uint32_t max_cpus = get_max_cpus();
+
+ // Populate midr values
+ std::vector<uint32_t> cpus_midr;
+ if(hwcaps & ARM_COMPUTE_CPU_FEATURE_HWCAP_CPUID)
+ {
+ cpus_midr = midr_from_cpuid(max_cpus);
+ }
+ if(cpus_midr.empty())
+ {
+ cpus_midr = midr_from_proc_cpuinfo(max_cpus);
+ }
+ if(cpus_midr.empty())
+ {
+ cpus_midr.resize(max_cpus, 0);
+ }
+
+ // Populate isa (Assume homogeneous ISA specification)
+ CpuIsaInfo isa = init_cpu_isa_from_hwcaps(hwcaps, hwcaps2, cpus_midr.back());
+
+ // Convert midr to models
+ std::vector<CpuModel> cpus_model;
+ std::transform(std::begin(cpus_midr), std::end(cpus_midr), std::back_inserter(cpus_model),
+ [](uint32_t midr) -> CpuModel { return midr_to_model(midr); });
+
+ CpuInfo info(isa, cpus_model);
+ return info;
+
+#elif(BARE_METAL) && defined(__aarch64__) /* !defined(BARE_METAL) && !defined(__APPLE__) && (defined(__arm__) || defined(__aarch64__)) */
+
+ // Assume single CPU in bare metal mode. Just read the ID register and feature bits directly.
+ uint64_t isar0 = 0, isar1 = 0, pfr0 = 0, svefr0 = 0, midr = 0;
+ ARM_COMPUTE_GET_FEATURE_REG(isar0, ID_AA64ISAR0_EL1);
+ ARM_COMPUTE_GET_FEATURE_REG(isar1, ID_AA64ISAR1_EL1);
+ ARM_COMPUTE_GET_FEATURE_REG(pfr0, ID_AA64PFR0_EL1);
+ ARM_COMPUTE_GET_FEATURE_REG(midr, MIDR_EL1);
+ if((pfr0 >> 32) & 0xf)
+ {
+ svefr0 = get_sve_feature_reg();
+ }
+
+ CpuIsaInfo isa = init_cpu_isa_from_regs(isar0, isar1, pfr0, svefr0, midr);
+ std::vector<CpuModel> cpus_model(1, midr_to_model(midr));
+ CpuInfo info(isa, cpus_model);
+ return info;
+#else /* #elif(BARE_METAL) && defined(__aarch64__) */
+ CpuInfo info(CpuIsaInfo(), { CpuModel::GENERIC });
+ return info;
+#endif /* !defined(BARE_METAL) && !defined(__APPLE__) && (defined(__arm__) || defined(__aarch64__)) */
+}
+
+CpuModel CpuInfo::cpu_model(uint32_t cpuid) const
+{
+ if(cpuid < _cpus.size())
+ {
+ return _cpus[cpuid];
+ }
+ return CpuModel::GENERIC;
+}
+
+CpuModel CpuInfo::cpu_model() const
+{
+#if defined(BARE_METAL) || defined(__APPLE__) || (!defined(__arm__) && !defined(__aarch64__))
+ return cpu_model(0);
+#else /* defined(BARE_METAL) || defined(__APPLE__) || (!defined(__arm__) && !defined(__aarch64__)) */
+ return cpu_model(sched_getcpu());
+#endif /* defined(BARE_METAL) || defined(__APPLE__) || (!defined(__arm__) && !defined(__aarch64__)) */
+}
+
+uint32_t CpuInfo::num_cpus() const
+{
+ return _cpus.size();
+}
+
+uint32_t num_threads_hint()
+{
+ unsigned int num_threads_hint = 1;
+
+#if !defined(BARE_METAL)
+ std::vector<std::string> cpus;
+ cpus.reserve(64);
+
+ // CPU part regex
+ regex_t cpu_part_rgx;
+ memset(&cpu_part_rgx, 0, sizeof(regex_t));
+ int ret_status = regcomp(&cpu_part_rgx, R"(.*CPU part.+/?\:[[:space:]]+([[:alnum:]]+).*)", REG_EXTENDED);
+ ARM_COMPUTE_UNUSED(ret_status);
+ ARM_COMPUTE_ERROR_ON_MSG(ret_status != 0, "Regex compilation failed.");
+
+ // Read cpuinfo and get occurrence of each core
+ std::ifstream cpuinfo_file("/proc/cpuinfo", std::ios::in);
+ if(cpuinfo_file.is_open())
+ {
+ std::string line;
+ while(bool(getline(cpuinfo_file, line)))
+ {
+ std::array<regmatch_t, 2> match;
+ if(regexec(&cpu_part_rgx, line.c_str(), 2, match.data(), 0) == 0)
+ {
+ cpus.emplace_back(line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so)));
+ }
+ }
+ }
+ regfree(&cpu_part_rgx);
+
+ // Get min number of threads
+ std::sort(std::begin(cpus), std::end(cpus));
+ auto least_frequent_cpu_occurences = [](const std::vector<std::string> &cpus) -> uint32_t
+ {
+ std::unordered_map<std::string, uint32_t> cpus_freq;
+ for(const auto &cpu : cpus)
+ {
+ cpus_freq[cpu]++;
+ }
+
+ uint32_t vmin = cpus.size() + 1;
+ for(const auto &cpu_freq : cpus_freq)
+ {
+ vmin = std::min(vmin, cpu_freq.second);
+ }
+ return vmin;
+ };
+
+ // Set thread hint
+ num_threads_hint = cpus.empty() ? std::thread::hardware_concurrency() : least_frequent_cpu_occurences(cpus);
+#endif /* !defined(BARE_METAL) */
+
+ return num_threads_hint;
+}
+} // namespace cpuinfo
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/common/cpuinfo/CpuInfo.h b/src/common/cpuinfo/CpuInfo.h
new file mode 100644
index 0000000000..f3056d2faf
--- /dev/null
+++ b/src/common/cpuinfo/CpuInfo.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2021 Arm Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef SRC_COMMON_CPUINFO_H
+#define SRC_COMMON_CPUINFO_H
+
+#include "src/common/cpuinfo/CpuIsaInfo.h"
+#include "src/common/cpuinfo/CpuModel.h"
+
+#include <string>
+#include <vector>
+
+namespace arm_compute
+{
+namespace cpuinfo
+{
+/** Aggregate class that contains CPU related information
+ *
+ * Contains information about the numbers of the CPUs, the model of each CPU,
+ * ISA related information and more
+ *
+ * @note We can safely assume that the ISA is common between different clusters of cores
+ */
+class CpuInfo
+{
+public:
+ /** Default constructor */
+ CpuInfo() = default;
+ /** Construct a new Cpu Info object
+ *
+ * @param[in] isa ISA capabilities information
+ * @param[in] cpus CPU models information
+ */
+ CpuInfo(CpuIsaInfo isa, std::vector<CpuModel> cpus);
+ /** CpuInfo builder function from system related information
+ *
+ * @return CpuInfo A populated CpuInfo structure
+ */
+ static CpuInfo build();
+
+public:
+ bool has_neon() const
+ {
+ return _isa.neon;
+ }
+ bool has_sve() const
+ {
+ return _isa.sve;
+ }
+ bool has_sve2() const
+ {
+ return _isa.sve2;
+ }
+ bool has_fp16() const
+ {
+ return _isa.fp16;
+ }
+ bool has_bf16() const
+ {
+ return _isa.bf16;
+ }
+ bool has_dotprod() const
+ {
+ return _isa.dot;
+ }
+ bool has_immla() const
+ {
+ return _isa.immla;
+ }
+ bool has_fmmla() const
+ {
+ return _isa.fmmla;
+ }
+
+ CpuModel cpu_model(uint32_t cpuid) const;
+ CpuModel cpu_model() const;
+ uint32_t num_cpus() const;
+
+private:
+ CpuIsaInfo _isa{};
+ std::vector<CpuModel> _cpus{};
+};
+
+/** Some systems have both big and small cores, this fuction computes the minimum number of cores
+ * that are exactly the same on the system. To maximize performance the library attempts to process
+ * workloads concurrently using as many threads as big cores are available on the system.
+ *
+ * @return The minumum number of common cores.
+ */
+uint32_t num_threads_hint();
+} // namespace cpuinfo
+} // namespace arm_compute
+#endif /* SRC_COMMON_CPUINFO_H */
diff --git a/src/common/cpuinfo/CpuIsaInfo.cpp b/src/common/cpuinfo/CpuIsaInfo.cpp
new file mode 100644
index 0000000000..d99f9aec29
--- /dev/null
+++ b/src/common/cpuinfo/CpuIsaInfo.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2021 Arm Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "src/common/cpuinfo/CpuIsaInfo.h"
+
+#include "arm_compute/core/Error.h"
+#include "src/common/cpuinfo/CpuModel.h"
+
+/* Arm Feature flags */
+#define ARM_COMPUTE_CPU_FEATURE_HWCAP_HALF (1 << 1)
+#define ARM_COMPUTE_CPU_FEATURE_HWCAP_NEON (1 << 12)
+
+/* Arm64 Feature flags */
+#define ARM_COMPUTE_CPU_FEATURE_HWCAP_ASIMD (1 << 1)
+#define ARM_COMPUTE_CPU_FEATURE_HWCAP_FPHP (1 << 9)
+#define ARM_COMPUTE_CPU_FEATURE_HWCAP_ASIMDHP (1 << 10)
+#define ARM_COMPUTE_CPU_FEATURE_HWCAP_ASIMDDP (1 << 20)
+#define ARM_COMPUTE_CPU_FEATURE_HWCAP_SVE (1 << 22)
+#define ARM_COMPUTE_CPU_FEATURE_HWCAP2_SVE2 (1 << 1)
+#define ARM_COMPUTE_CPU_FEATURE_HWCAP2_SVEI8MM (1 << 9)
+#define ARM_COMPUTE_CPU_FEATURE_HWCAP2_SVEF32MM (1 << 10)
+#define ARM_COMPUTE_CPU_FEATURE_HWCAP2_SVEBF16 (1 << 12)
+#define ARM_COMPUTE_CPU_FEATURE_HWCAP2_I8MM (1 << 13)
+#define ARM_COMPUTE_CPU_FEATURE_HWCAP2_BF16 (1 << 14)
+
+namespace arm_compute
+{
+namespace cpuinfo
+{
+namespace
+{
+#if defined(__arm__)
+void decode_hwcaps(CpuIsaInfo &isa, const uint32_t hwcaps, const uint32_t hwcaps2)
+{
+ ARM_COMPUTE_UNUSED(hwcaps2);
+
+ if(hwcaps & ARM_COMPUTE_CPU_FEATURE_HWCAP_HALF)
+ {
+ isa.fp16 = true;
+ }
+ if(hwcaps & ARM_COMPUTE_CPU_FEATURE_HWCAP_NEON)
+ {
+ isa.neon = true;
+ }
+}
+#elif defined(__aarch64__)
+void decode_hwcaps(CpuIsaInfo &isa, const uint32_t hwcaps, const uint32_t hwcaps2)
+{
+ // High-level SIMD support
+ if(hwcaps & ARM_COMPUTE_CPU_FEATURE_HWCAP_ASIMD)
+ {
+ isa.neon = true;
+ }
+ if(hwcaps & ARM_COMPUTE_CPU_FEATURE_HWCAP_SVE)
+ {
+ isa.sve = true;
+ }
+ if(hwcaps2 & ARM_COMPUTE_CPU_FEATURE_HWCAP2_SVE2)
+ {
+ isa.sve2 = true;
+ }
+
+ // Data-type support
+ const uint32_t fp16_support_mask = ARM_COMPUTE_CPU_FEATURE_HWCAP_FPHP | ARM_COMPUTE_CPU_FEATURE_HWCAP_ASIMDHP;
+ if(hwcaps & fp16_support_mask)
+ {
+ isa.fp16 = true;
+ }
+ if(hwcaps2 & ARM_COMPUTE_CPU_FEATURE_HWCAP2_BF16)
+ {
+ isa.bf16 = true;
+ }
+
+ // Instruction extensions
+ if(hwcaps & ARM_COMPUTE_CPU_FEATURE_HWCAP_ASIMDDP)
+ {
+ isa.dot = true;
+ }
+ if(hwcaps2 & ARM_COMPUTE_CPU_FEATURE_HWCAP2_I8MM)
+ {
+ isa.immla = true;
+ }
+ if(hwcaps2 & ARM_COMPUTE_CPU_FEATURE_HWCAP2_SVEF32MM)
+ {
+ isa.fmmla = true;
+ }
+}
+#else /* defined(__aarch64__) */
+void decode_hwcaps(CpuIsaInfo &isa, const uint32_t hwcaps, const uint32_t hwcaps2)
+{
+ ARM_COMPUTE_UNUSED(isa, hwcaps, hwcaps2);
+}
+#endif /* defined(__aarch64__) */
+
+void decode_regs(CpuIsaInfo &isa, const uint64_t isar0, const uint64_t isar1, const uint64_t pfr0, const uint64_t svefr0)
+{
+ // High-level SIMD support
+ if((pfr0 >> 32) & 0xf)
+ {
+ isa.sve = true;
+ }
+ if(svefr0 & 0xf)
+ {
+ isa.sve2 = true;
+ }
+
+ // Data-type support
+ if((pfr0 >> 16) & 0xf)
+ {
+ isa.fp16 = true;
+ }
+ if((isar1 >> 44) & 0xf)
+ {
+ isa.bf16 = true;
+ }
+
+ // Instruction extensions
+ if((isar0 >> 44) & 0xf)
+ {
+ isa.dot = true;
+ }
+ if((isar1 >> 48) & 0xf)
+ {
+ isa.immla = true;
+ }
+ if((svefr0 >> 52) & 0xf)
+ {
+ isa.fmmla = true;
+ }
+}
+
+/** Handle features from whitelisted models in case of problematic kernels
+ *
+ * @param[in, out] isa ISA to update
+ * @param[in] model CPU model type
+ */
+void whitelisted_model_features(CpuIsaInfo &isa, CpuModel model)
+{
+ if(isa.dot == false)
+ {
+ isa.dot = model_supports_dot(model);
+ }
+ if(isa.fp16 == false)
+ {
+ isa.fp16 = model_supports_fp16(model);
+ }
+ if(isa.sve == false)
+ {
+ isa.sve = model_supports_sve(model);
+ }
+}
+} // namespace
+
+CpuIsaInfo init_cpu_isa_from_hwcaps(uint32_t hwcaps, uint32_t hwcaps2, uint32_t midr)
+{
+ CpuIsaInfo isa;
+
+ decode_hwcaps(isa, hwcaps, hwcaps2);
+
+ const CpuModel model = midr_to_model(midr);
+ whitelisted_model_features(isa, model);
+
+ return isa;
+}
+
+CpuIsaInfo init_cpu_isa_from_regs(uint64_t isar0, uint64_t isar1, uint64_t pfr0, uint64_t svefr0, uint64_t midr)
+{
+ CpuIsaInfo isa;
+
+ decode_regs(isa, isar0, isar1, pfr0, svefr0);
+
+ const CpuModel model = midr_to_model(midr);
+ whitelisted_model_features(isa, model);
+
+ return isa;
+}
+} // namespace cpuinfo
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/common/cpuinfo/CpuIsaInfo.h b/src/common/cpuinfo/CpuIsaInfo.h
new file mode 100644
index 0000000000..1125f766dd
--- /dev/null
+++ b/src/common/cpuinfo/CpuIsaInfo.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2021 Arm Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef SRC_COMMON_CPUINFO_CPUISAINFO_H
+#define SRC_COMMON_CPUINFO_CPUISAINFO_H
+
+#include <cstdint>
+
+namespace arm_compute
+{
+namespace cpuinfo
+{
+/** CPU ISA (Instruction Set Architecture) information
+ *
+ * Contains ISA related information around the Arm architecture
+ */
+struct CpuIsaInfo
+{
+ /* SIMD extension support */
+ bool neon{ false };
+ bool sve{ false };
+ bool sve2{ false };
+
+ /* Data-type extensions support */
+ bool fp16{ false };
+ bool bf16{ false };
+
+ /* Instruction support */
+ bool dot{ false };
+ bool immla{ false };
+ bool fmmla{ false };
+};
+
+/** Identify ISA related information through system information
+ *
+ * @param[in] hwcaps HWCAPS feature information
+ * @param[in] hwcaps2 HWCAPS2 feature information
+ * @param[in] midr MIDR value
+ *
+ * @return CpuIsaInfo A populated ISA feature structure
+ */
+CpuIsaInfo init_cpu_isa_from_hwcaps(uint32_t hwcaps, uint32_t hwcaps2, uint32_t midr);
+
+/** Identify ISA related information through register information
+ *
+ * @param[in] isar0 Value of Instruction Set Attribute Register 0 (ID_AA64ISAR0_EL1)
+ * @param[in] isar1 Value of Instruction Set Attribute Register 1 (ID_AA64ISAR1_EL1)
+ * @param[in] pfr0 Value of Processor Feature Register 0 (ID_AA64PFR0_EL1)
+ * @param[in] svefr0 Value of SVE feature ID register 0 (ID_AA64ZFR0_EL1)
+ * @param[in] midr Value of Main ID Register (MIDR)
+ *
+ * @return CpuIsaInfo A populated ISA feature structure
+ */
+CpuIsaInfo init_cpu_isa_from_regs(uint64_t isar0, uint64_t isar1, uint64_t pfr0, uint64_t svefr0, uint64_t midr);
+} // namespace cpuinfo
+} // namespace arm_compute
+
+#endif /* SRC_COMMON_CPUINFO_CPUISAINFO_H */
diff --git a/src/common/cpuinfo/CpuModel.cpp b/src/common/cpuinfo/CpuModel.cpp
new file mode 100644
index 0000000000..9f4d5d1433
--- /dev/null
+++ b/src/common/cpuinfo/CpuModel.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2021 Arm Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "src/common/cpuinfo/CpuModel.h"
+
+namespace arm_compute
+{
+namespace cpuinfo
+{
+std::string cpu_model_to_string(CpuModel model)
+{
+ switch(model)
+ {
+#define X(MODEL) \
+case CpuModel::MODEL: \
+ return #MODEL;
+ ARM_COMPUTE_CPU_MODEL_LIST
+#undef X
+ default:
+ {
+ return std::string("GENERIC");
+ }
+ };
+}
+
+bool model_supports_fp16(CpuModel model)
+{
+ switch(model)
+ {
+ case CpuModel::GENERIC_FP16:
+ case CpuModel::GENERIC_FP16_DOT:
+ case CpuModel::A55r1:
+ case CpuModel::X1:
+ case CpuModel::KLEIN:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool model_supports_dot(CpuModel model)
+{
+ switch(model)
+ {
+ case CpuModel::GENERIC_FP16_DOT:
+ case CpuModel::A55r1:
+ case CpuModel::X1:
+ case CpuModel::KLEIN:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool model_supports_sve(CpuModel model)
+{
+ switch(model)
+ {
+ case CpuModel::KLEIN:
+ return true;
+ default:
+ return false;
+ }
+}
+
+CpuModel midr_to_model(uint32_t midr)
+{
+ CpuModel model = CpuModel::GENERIC;
+
+ // Unpack variant and CPU ID
+ const int implementer = (midr >> 24) & 0xFF;
+ const int variant = (midr >> 20) & 0xF;
+ const int cpunum = (midr >> 4) & 0xFFF;
+
+ if(implementer == 0x41) // Arm CPUs
+ {
+ // Only CPUs we have code paths for are detected. All other CPUs can be safely classed as "GENERIC"
+ switch(cpunum)
+ {
+ case 0xd03: // A53
+ case 0xd04: // A35
+ model = CpuModel::A53;
+ break;
+ case 0xd05: // A55
+ if(variant != 0)
+ {
+ model = CpuModel::A55r1;
+ }
+ else
+ {
+ model = CpuModel::A55r0;
+ }
+ break;
+ case 0xd09: // A73
+ model = CpuModel::A73;
+ break;
+ case 0xd0a: // A75
+ if(variant != 0)
+ {
+ model = CpuModel::GENERIC_FP16_DOT;
+ }
+ else
+ {
+ model = CpuModel::GENERIC_FP16;
+ }
+ break;
+ case 0xd06: // A65
+ case 0xd0b: // A76
+ case 0xd0c: // N1
+ case 0xd0d: // A77
+ case 0xd0e: // A76AE
+ case 0xd41: // A78
+ case 0xd42: // A78AE
+ case 0xd4a: // E1
+ model = CpuModel::GENERIC_FP16_DOT;
+ break;
+ case 0xd44: // X1
+ model = CpuModel::X1;
+ break;
+ case 0xd46:
+ model = CpuModel::KLEIN;
+ break;
+ default:
+ model = CpuModel::GENERIC;
+ break;
+ }
+ }
+ else if(implementer == 0x48)
+ {
+ // Only CPUs we have code paths for are detected. All other CPUs can be safely classed as "GENERIC"
+ switch(cpunum)
+ {
+ case 0xd40: // A76
+ model = CpuModel::GENERIC_FP16_DOT;
+ break;
+ default:
+ model = CpuModel::GENERIC;
+ break;
+ }
+ }
+ else if(implementer == 0x51)
+ {
+ // Only CPUs we have code paths for are detected. All other CPUs can be safely classed as "GENERIC"
+ switch(cpunum)
+ {
+ case 0x800: // A73
+ model = CpuModel::A73;
+ break;
+ case 0x801: // A53
+ model = CpuModel::A53;
+ break;
+ case 0x803: // A55r0
+ model = CpuModel::A55r0;
+ break;
+ case 0x804: // A76
+ model = CpuModel::GENERIC_FP16_DOT;
+ break;
+ case 0x805: // A55r1
+ model = CpuModel::A55r1;
+ break;
+ default:
+ model = CpuModel::GENERIC;
+ break;
+ }
+ }
+
+ return model;
+}
+} // namespace cpuinfo
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/common/cpuinfo/CpuModel.h b/src/common/cpuinfo/CpuModel.h
new file mode 100644
index 0000000000..071efc4b3f
--- /dev/null
+++ b/src/common/cpuinfo/CpuModel.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2021 Arm Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef SRC_COMMON_CPUINFO_CPUMODEL_H
+#define SRC_COMMON_CPUINFO_CPUMODEL_H
+
+#include <cstdint>
+#include <string>
+
+#include "arm_compute/core/CPP/CPPTypes.h"
+
+namespace arm_compute
+{
+namespace cpuinfo
+{
+using CpuModel = arm_compute::CPUModel;
+
+/** Convert a CPU model value to a string
+ *
+ * @param model CpuModel value to be converted
+ *
+ * @return String representing the corresponding CpuModel
+ */
+std::string cpu_model_to_string(CpuModel model);
+
+/** Extract the model type from the MIDR value
+ *
+ * @param[in] midr MIDR information
+ *
+ * @return CpuModel a mapped CPU model
+ */
+CpuModel midr_to_model(uint32_t midr);
+
+/** Check if a model supports half-precision floating point arithmetic
+ *
+ * @note This is used in case of old kernel configurations where some capabilities are not exposed.
+ *
+ * @param[in] model Model to check for whitelisted capabilities
+ */
+bool model_supports_fp16(CpuModel model);
+
+/** Check if a model supports SVE
+ *
+ * @note This is used in case of old kernel configurations where some capabilities are not exposed.
+ *
+ * @param[in] model Model to check for whitelisted capabilities
+ */
+bool model_supports_sve(CpuModel model);
+
+/** Check if a model supports dot product
+ *
+ * @note This is used in case of old kernel configurations where some capabilities are not exposed.
+ *
+ * @param[in] model Model to check for whitelisted capabilities
+ */
+bool model_supports_dot(CpuModel model);
+} // namespace cpuinfo
+} // namespace arm_compute
+#endif /* SRC_COMMON_CPUINFO_CPUMODEL_H */
diff --git a/src/runtime/DeviceProperties.cpp b/src/common/cpuinfo/target/CpuInfoSveUtils.cpp
index ec9f4a16ed..750a1b01d1 100644
--- a/src/runtime/DeviceProperties.cpp
+++ b/src/common/cpuinfo/target/CpuInfoSveUtils.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019-2020 Arm Limited.
+ * Copyright (c) 2021 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -21,14 +21,20 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#include "arm_compute/runtime/DeviceProperties.h"
-
-#include "src/runtime/CPUUtils.h"
+#include "src/common/cpuinfo/target/CpuInfoSveUtils.h"
namespace arm_compute
{
-DeviceProperties::DeviceProperties()
+namespace cpuinfo
+{
+uint64_t get_sve_feature_reg()
{
- utils::cpu::get_cpu_configuration(cpu_info);
+ uint64_t reg = 0;
+#if defined(ENABLE_SVE)
+ __asm __volatile("MRS %0, ID_AA64ZFR0_EL1"
+ : "=r"(reg));
+#endif /* defined(DENABLE_SVE) */
+ return reg;
}
+} // namespace cpuinfo
} // namespace arm_compute
diff --git a/arm_compute/runtime/DeviceProperties.h b/src/common/cpuinfo/target/CpuInfoSveUtils.h
index 807b53eae4..73862b131c 100644
--- a/arm_compute/runtime/DeviceProperties.h
+++ b/src/common/cpuinfo/target/CpuInfoSveUtils.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 Arm Limited.
+ * Copyright (c) 2021 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -21,21 +21,20 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#ifndef ARM_COMPUTE_DEVICE_PROPERTIES_H
-#define ARM_COMPUTE_DEVICE_PROPERTIES_H
+#ifndef SRC_COMMON_CPUINFO_TARGET_CPUINFO_SVE_UTILS_H
+#define SRC_COMMON_CPUINFO_TARGET_CPUINFO_SVE_UTILS_H
-#include "arm_compute/core/CPP/CPPTypes.h"
+#include <cstdint>
namespace arm_compute
{
-/** Device properties */
-struct DeviceProperties
+namespace cpuinfo
{
- std::string name{ "unknown" };
- CPUInfo cpu_info{}; // initialised upon creating in the constructor
-
- DeviceProperties();
-};
-
+/** Returns the contents of the SVE feature register (ID_AA64ZFR0_EL1)
+ *
+ * @return uint64_t The value of the register
+ */
+uint64_t get_sve_feature_reg();
+} // namespace cpuinfo
} // namespace arm_compute
-#endif /*ARM_COMPUTE_DEVICE_PROPERTIES_H */
+#endif /* SRC_COMMON_CPUINFO_CPUISAINFO_H */
diff --git a/src/core/CPP/CPPTypes.cpp b/src/core/CPP/CPPTypes.cpp
index 0850df29fd..edcb9cb1ba 100644
--- a/src/core/CPP/CPPTypes.cpp
+++ b/src/core/CPP/CPPTypes.cpp
@@ -25,105 +25,67 @@
#include "arm_compute/core/CPP/CPPTypes.h"
#include "arm_compute/core/Error.h"
+#include "src/common/cpuinfo/CpuInfo.h"
-#if !defined(BARE_METAL)
-#include <sched.h>
-#endif /* defined(BARE_METAL) */
-
-using namespace arm_compute;
-
-void CPUInfo::set_fp16(const bool fp16)
+namespace arm_compute
{
- _fp16 = fp16;
-}
-
-void CPUInfo::set_dotprod(const bool dotprod)
+struct CPUInfo::Impl
{
- _dotprod = dotprod;
-}
+ cpuinfo::CpuInfo info{};
+ unsigned int L1_cache_size = 32768;
+ unsigned int L2_cache_size = 262144;
+};
-void CPUInfo::set_sve(const bool sve)
+CPUInfo::CPUInfo()
+ : _impl(std::make_unique<Impl>())
{
- _sve = sve;
+ _impl->info = cpuinfo::CpuInfo::build();
}
-void CPUInfo::set_cpu_model(unsigned int cpuid, CPUModel model)
-{
- ARM_COMPUTE_ERROR_ON(cpuid >= _percpu.size());
- if(_percpu.size() > cpuid)
- {
- _percpu[cpuid] = model;
- }
-}
+CPUInfo::~CPUInfo() = default;
unsigned int CPUInfo::get_cpu_num() const
{
- return _percpu.size();
+ return _impl->info.num_cpus();
}
bool CPUInfo::has_sve() const
{
- return _sve;
+ return _impl->info.has_sve();
}
bool CPUInfo::has_fp16() const
{
- return _fp16;
-}
-
-bool CPUInfo::has_dotprod() const
-{
- return _dotprod;
-}
-
-CPUModel CPUInfo::get_cpu_model(unsigned int cpuid) const
-{
- if(cpuid < _percpu.size())
- {
- return _percpu[cpuid];
- }
- return CPUModel::GENERIC;
+ return _impl->info.has_fp16();
}
-unsigned int CPUInfo::get_L1_cache_size() const
+bool CPUInfo::has_bf16() const
{
- return _L1_cache_size;
+ return _impl->info.has_bf16();
}
-void CPUInfo::set_L1_cache_size(unsigned int size)
-{
- _L1_cache_size = size;
-}
-
-unsigned int CPUInfo::get_L2_cache_size() const
+bool CPUInfo::has_dotprod() const
{
- return _L2_cache_size;
+ return _impl->info.has_dotprod();
}
-void CPUInfo::set_L2_cache_size(unsigned int size)
+CPUModel CPUInfo::get_cpu_model() const
{
- _L2_cache_size = size;
+ return _impl->info.cpu_model();
}
-void CPUInfo::set_cpu_num(unsigned int cpu_count)
+CPUModel CPUInfo::get_cpu_model(unsigned int cpuid) const
{
- _percpu.resize(cpu_count);
+ return _impl->info.cpu_model(cpuid);
}
-CPUInfo::CPUInfo()
- : _percpu(1)
+unsigned int CPUInfo::get_L1_cache_size() const
{
- // The core library knows nothing about the CPUs so we set only 1 CPU to be generic.
- // The runtime NESCheduler will initialise this vector with the correct CPU models.
- // See void detect_cpus_configuration(CPUInfo &cpuinfo) in CPPUtils.h
- _percpu[0] = CPUModel::GENERIC;
+ return _impl->L1_cache_size;
}
-CPUModel CPUInfo::get_cpu_model() const
+unsigned int CPUInfo::get_L2_cache_size() const
{
-#if defined(BARE_METAL) || defined(__APPLE__) || (!defined(__arm__) && !defined(__aarch64__))
- return get_cpu_model(0);
-#else /* defined(BARE_METAL) || defined(__APPLE__) || (!defined(__arm__) && !defined(__aarch64__)) */
- return get_cpu_model(sched_getcpu());
-#endif /* defined(BARE_METAL) || defined(__APPLE__) || (!defined(__arm__) && !defined(__aarch64__)) */
+ return _impl->L2_cache_size;
}
+} // namespace arm_compute
diff --git a/src/core/cpu/kernels/assembly/arm_gemm.hpp b/src/core/cpu/kernels/assembly/arm_gemm.hpp
index 624e9e94dc..81e355d6b3 100644
--- a/src/core/cpu/kernels/assembly/arm_gemm.hpp
+++ b/src/core/cpu/kernels/assembly/arm_gemm.hpp
@@ -25,6 +25,7 @@
#include <cstring>
#include <memory>
+#include <vector>
#include "arm_gemm_local.hpp"
#include "gemm_common.hpp"
diff --git a/src/cpu/CpuContext.cpp b/src/cpu/CpuContext.cpp
index b9a6999f84..18fa2e7469 100644
--- a/src/cpu/CpuContext.cpp
+++ b/src/cpu/CpuContext.cpp
@@ -26,7 +26,6 @@
#include "arm_compute/core/CPP/CPPTypes.h"
#include "src/cpu/CpuQueue.h"
#include "src/cpu/CpuTensor.h"
-#include "src/runtime/CPUUtils.h"
#include <cstdlib>
#include <malloc.h>
@@ -142,7 +141,6 @@ CpuCapabilities populate_capabilities(AclTargetCapabilities external_caps,
{
// Extract legacy structure
CPUInfo cpu_info;
- arm_compute::utils::cpu::get_cpu_configuration(cpu_info);
CpuCapabilities caps;
if(external_caps != AclCpuCapabilitiesAuto)
diff --git a/src/runtime/CPP/CPPScheduler.cpp b/src/runtime/CPP/CPPScheduler.cpp
index 3bd80eb51d..f112d456c7 100644
--- a/src/runtime/CPP/CPPScheduler.cpp
+++ b/src/runtime/CPP/CPPScheduler.cpp
@@ -29,7 +29,6 @@
#include "arm_compute/core/Log.h"
#include "arm_compute/core/Utils.h"
#include "arm_compute/core/utils/misc/Utility.h"
-#include "src/runtime/CPUUtils.h"
#include "support/Mutex.h"
#include <atomic>
diff --git a/src/runtime/CPUUtils.cpp b/src/runtime/CPUUtils.cpp
deleted file mode 100644
index 2bcba72f77..0000000000
--- a/src/runtime/CPUUtils.cpp
+++ /dev/null
@@ -1,556 +0,0 @@
-/*
- * Copyright (c) 2018-2021 Arm Limited.
- *
- * SPDX-License-Identifier: MIT
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-#include "src/runtime/CPUUtils.h"
-
-#include "arm_compute/core/CPP/CPPTypes.h"
-#include "arm_compute/core/Error.h"
-#include "arm_compute/core/Log.h"
-#include "support/StringSupport.h"
-
-#include <algorithm>
-#include <array>
-#include <cstdlib>
-#include <cstring>
-#include <fstream>
-#include <map>
-
-#if !defined(BARE_METAL)
-/* C++ std::regex takes up a lot of space in the standalone builds */
-#include <regex.h>
-#include <thread>
-#endif /* !defined(BARE_METAL) */
-
-#if !defined(BARE_METAL) && !defined(__APPLE__) && (defined(__arm__) || defined(__aarch64__))
-#include <sys/auxv.h>
-
-/* Get HWCAP bits from asm/hwcap.h */
-#include <asm/hwcap.h>
-#endif /* !defined(BARE_METAL) && !defined(__APPLE__) && (defined(__arm__) || defined(__aarch64__)) */
-
-/* Make sure the bits we care about are defined, just in case asm/hwcap.h is
- * out of date (or for bare metal mode) */
-#ifndef HWCAP_ASIMDHP
-#define HWCAP_ASIMDHP (1 << 10) // NOLINT
-#endif /* HWCAP_ASIMDHP */
-
-#ifndef HWCAP_CPUID
-#define HWCAP_CPUID (1 << 11) // NOLINT
-#endif /* HWCAP_CPUID */
-
-#ifndef HWCAP_ASIMDDP
-#define HWCAP_ASIMDDP (1 << 20) // NOLINT
-#endif /* HWCAP_ASIMDDP */
-
-#ifndef HWCAP_SVE
-#define HWCAP_SVE (1 << 22) // NOLINT
-#endif /* HWCAP_SVE */
-
-namespace
-{
-using namespace arm_compute;
-
-#if !defined(BARE_METAL) && !defined(__APPLE__) && (defined(__arm__) || defined(__aarch64__))
-
-bool model_supports_sve(CPUModel model)
-{
- switch(model)
- {
- case CPUModel::KLEIN:
- return true;
- default:
- return false;
- }
-}
-
-bool model_supports_dot(CPUModel model)
-{
- switch(model)
- {
- case CPUModel::GENERIC_FP16_DOT:
- case CPUModel::A55r1:
- case CPUModel::X1:
- case CPUModel::KLEIN:
- return true;
- default:
- return false;
- }
-}
-
-bool model_supports_fp16(CPUModel model)
-{
- switch(model)
- {
- case CPUModel::GENERIC_FP16:
- case CPUModel::GENERIC_FP16_DOT:
- case CPUModel::A55r1:
- case CPUModel::X1:
- case CPUModel::KLEIN:
- return true;
- default:
- return false;
- }
-}
-
-/* Convert an MIDR register value to a CPUModel enum value. */
-CPUModel midr_to_model(const unsigned int midr)
-{
- CPUModel model = CPUModel::GENERIC;
-
- // Unpack variant and CPU ID
- const int implementer = (midr >> 24) & 0xFF;
- const int variant = (midr >> 20) & 0xF;
- const int cpunum = (midr >> 4) & 0xFFF;
-
- if(implementer == 0x41) // Arm CPUs
- {
- // Only CPUs we have code paths for are detected. All other CPUs can be safely classed as "GENERIC"
- switch(cpunum)
- {
- case 0xd03: // A53
- case 0xd04: // A35
- model = CPUModel::A53;
- break;
- case 0xd05: // A55
- if(variant != 0)
- {
- model = CPUModel::A55r1;
- }
- else
- {
- model = CPUModel::A55r0;
- }
- break;
- case 0xd09: // A73
- model = CPUModel::A73;
- break;
- case 0xd0a: // A75
- if(variant != 0)
- {
- model = CPUModel::GENERIC_FP16_DOT;
- }
- else
- {
- model = CPUModel::GENERIC_FP16;
- }
- break;
- case 0xd06: // A65
- case 0xd0b: // A76
- case 0xd0c: // N1
- case 0xd0d: // A77
- case 0xd41: // A78
- model = CPUModel::GENERIC_FP16_DOT;
- break;
- case 0xd44: // X1
- model = CPUModel::X1;
- break;
- case 0xd46:
- model = CPUModel::KLEIN;
- break;
- default:
- model = CPUModel::GENERIC;
- break;
- }
- }
- else if(implementer == 0x48)
- {
- // Only CPUs we have code paths for are detected. All other CPUs can be safely classed as "GENERIC"
- switch(cpunum)
- {
- case 0xd40: // A76
- model = CPUModel::GENERIC_FP16_DOT;
- break;
- default:
- model = CPUModel::GENERIC;
- break;
- }
- }
- else if(implementer == 0x51)
- {
- // Only CPUs we have code paths for are detected. All other CPUs can be safely classed as "GENERIC"
- switch(cpunum)
- {
- case 0x800: // A73
- model = CPUModel::A73;
- break;
- case 0x801: // A53
- model = CPUModel::A53;
- break;
- case 0x803: // A55r0
- model = CPUModel::A55r0;
- break;
- case 0x804: // A76
- model = CPUModel::GENERIC_FP16_DOT;
- break;
- case 0x805: // A55r1
- model = CPUModel::A55r1;
- break;
- default:
- model = CPUModel::GENERIC;
- break;
- }
- }
-
- return model;
-}
-
-void populate_models_cpuid(std::vector<CPUModel> &cpusv)
-{
- // If the CPUID capability is present, MIDR information is provided in /sys. Use that to populate the CPU model table.
- uint32_t i = 0;
- for(auto &c : cpusv)
- {
- std::stringstream str;
- str << "/sys/devices/system/cpu/cpu" << i++ << "/regs/identification/midr_el1";
- std::ifstream file;
- file.open(str.str(), std::ios::in);
- if(file.is_open())
- {
- std::string line;
- if(bool(getline(file, line)))
- {
- const uint32_t midr = support::cpp11::stoul(line, nullptr, support::cpp11::NumericBase::BASE_16);
- c = midr_to_model(midr & 0xffffffff);
- }
- }
- }
-}
-
-void populate_models_cpuinfo(std::vector<CPUModel> &cpusv)
-{
- regex_t proc_regex;
- regex_t imp_regex;
- regex_t var_regex;
- regex_t part_regex;
- regex_t rev_regex;
-
- memset(&proc_regex, 0, sizeof(regex_t));
- memset(&imp_regex, 0, sizeof(regex_t));
- memset(&var_regex, 0, sizeof(regex_t));
- memset(&part_regex, 0, sizeof(regex_t));
- memset(&rev_regex, 0, sizeof(regex_t));
-
- int ret_status = 0;
- // If "long-form" cpuinfo is present, parse that to populate models.
- ret_status |= regcomp(&proc_regex, R"(^processor.*([[:digit:]]+)$)", REG_EXTENDED);
- ret_status |= regcomp(&imp_regex, R"(^CPU implementer.*0x(..)$)", REG_EXTENDED);
- ret_status |= regcomp(&var_regex, R"(^CPU variant.*0x(.)$)", REG_EXTENDED);
- ret_status |= regcomp(&part_regex, R"(^CPU part.*0x(...)$)", REG_EXTENDED);
- ret_status |= regcomp(&rev_regex, R"(^CPU revision.*([[:digit:]]+)$)", REG_EXTENDED);
- ARM_COMPUTE_UNUSED(ret_status);
- ARM_COMPUTE_ERROR_ON_MSG(ret_status != 0, "Regex compilation failed.");
-
- std::ifstream file;
- file.open("/proc/cpuinfo", std::ios::in);
-
- if(file.is_open())
- {
- std::string line;
- int midr = 0;
- int curcpu = -1;
- const int num_cpus = static_cast<int>(cpusv.size());
-
- while(bool(getline(file, line)))
- {
- std::array<regmatch_t, 2> match;
- ret_status = regexec(&proc_regex, line.c_str(), 2, match.data(), 0);
- if(ret_status == 0)
- {
- std::string id = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
- int newcpu = support::cpp11::stoi(id, nullptr);
-
- if(curcpu >= 0 && midr == 0)
- {
- // Matched a new CPU ID without any description of the previous one - looks like old format.
- return;
- }
-
- if(curcpu >= 0 && curcpu < num_cpus)
- {
- cpusv[curcpu] = midr_to_model(midr);
- }
- else
- {
- ARM_COMPUTE_LOG_INFO_MSG_CORE("Trying to populate a core id with id greater than the expected number of cores!");
- }
-
- midr = 0;
- curcpu = newcpu;
-
- continue;
- }
-
- ret_status = regexec(&imp_regex, line.c_str(), 2, match.data(), 0);
- if(ret_status == 0)
- {
- std::string subexp = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
- int impv = support::cpp11::stoi(subexp, nullptr, support::cpp11::NumericBase::BASE_16);
- midr |= (impv << 24);
-
- continue;
- }
-
- ret_status = regexec(&var_regex, line.c_str(), 2, match.data(), 0);
- if(ret_status == 0)
- {
- std::string subexp = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
- int varv = support::cpp11::stoi(subexp, nullptr, support::cpp11::NumericBase::BASE_16);
- midr |= (varv << 20);
-
- continue;
- }
-
- ret_status = regexec(&part_regex, line.c_str(), 2, match.data(), 0);
- if(ret_status == 0)
- {
- std::string subexp = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
- int partv = support::cpp11::stoi(subexp, nullptr, support::cpp11::NumericBase::BASE_16);
- midr |= (partv << 4);
-
- continue;
- }
-
- ret_status = regexec(&rev_regex, line.c_str(), 2, match.data(), 0);
- if(ret_status == 0)
- {
- std::string subexp = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
- int regv = support::cpp11::stoi(subexp, nullptr);
- midr |= (regv);
- midr |= (0xf << 16);
-
- continue;
- }
- }
-
- if(curcpu >= 0 && curcpu < num_cpus)
- {
- cpusv[curcpu] = midr_to_model(midr);
- }
- else
- {
- ARM_COMPUTE_LOG_INFO_MSG_CORE("Trying to populate a core id with id greater than the expected number of cores!");
- }
- }
-
- // Free allocated memory
- regfree(&proc_regex);
- regfree(&imp_regex);
- regfree(&var_regex);
- regfree(&part_regex);
- regfree(&rev_regex);
-}
-
-int get_max_cpus()
-{
- int max_cpus = 1;
- std::ifstream CPUspresent;
- CPUspresent.open("/sys/devices/system/cpu/present", std::ios::in);
- bool success = false;
-
- if(CPUspresent.is_open())
- {
- std::string line;
-
- if(bool(getline(CPUspresent, line)))
- {
- /* The content of this file is a list of ranges or single values, e.g.
- * 0-5, or 1-3,5,7 or similar. As we are interested in the
- * max valid ID, we just need to find the last valid
- * delimiter ('-' or ',') and parse the integer immediately after that.
- */
- auto startfrom = line.begin();
-
- for(auto i = line.begin(); i < line.end(); ++i)
- {
- if(*i == '-' || *i == ',')
- {
- startfrom = i + 1;
- }
- }
-
- line.erase(line.begin(), startfrom);
-
- max_cpus = support::cpp11::stoi(line, nullptr) + 1;
- success = true;
- }
- }
-
- // Return std::thread::hardware_concurrency() as a fallback.
- if(!success)
- {
- max_cpus = std::thread::hardware_concurrency();
- }
- return max_cpus;
-}
-#endif /* !defined(BARE_METAL) && !defined(__APPLE__) && (defined(__arm__) || defined(__aarch64__)) */
-
-} // namespace
-
-namespace arm_compute
-{
-namespace utils
-{
-namespace cpu
-{
-void get_cpu_configuration(CPUInfo &cpuinfo)
-{
-#if !defined(BARE_METAL) && !defined(__APPLE__) && (defined(__arm__) || defined(__aarch64__))
- bool cpuid = false;
- bool hwcaps_fp16_support = false;
- bool hwcaps_dot_support = false;
- bool hwcaps_sve = false;
- const uint32_t hwcaps = getauxval(AT_HWCAP);
-
- if((hwcaps & HWCAP_CPUID) != 0)
- {
- cpuid = true;
- }
-
- if((hwcaps & HWCAP_ASIMDHP) != 0)
- {
- hwcaps_fp16_support = true;
- }
-
-#if defined(__aarch64__)
- if((hwcaps & HWCAP_ASIMDDP) != 0)
- {
- hwcaps_dot_support = true;
- }
-
- if((hwcaps & HWCAP_SVE) != 0)
- {
- hwcaps_sve = true;
- }
-#endif /* defined(__aarch64__) */
-
- const unsigned int max_cpus = get_max_cpus();
- cpuinfo.set_cpu_num(max_cpus);
- std::vector<CPUModel> percpu(max_cpus, CPUModel::GENERIC);
- if(cpuid)
- {
- populate_models_cpuid(percpu);
- }
- else
- {
- populate_models_cpuinfo(percpu);
- }
- int j(0);
- // Update dot product and FP16 support if one of the CPUs support these features
- // We assume that the system does not have mixed architectures
- bool one_supports_dot = false;
- bool one_supports_fp16 = false;
- bool one_supports_sve = false;
- for(const auto &v : percpu)
- {
- one_supports_dot = one_supports_dot || model_supports_dot(v);
- one_supports_fp16 = one_supports_fp16 || model_supports_fp16(v);
- one_supports_sve = one_supports_sve || model_supports_sve(v);
- cpuinfo.set_cpu_model(j++, v);
- }
- cpuinfo.set_dotprod(one_supports_dot || hwcaps_dot_support);
- cpuinfo.set_fp16(one_supports_fp16 || hwcaps_fp16_support);
- cpuinfo.set_sve(one_supports_sve || hwcaps_sve);
-#elif(BARE_METAL) && defined(__aarch64__) /* !defined(BARE_METAL) && !defined(__APPLE__) && (defined(__arm__) || defined(__aarch64__)) */
- cpuinfo.set_cpu_num(1);
- const CPUModel cpumodel{ CPUModel::GENERIC };
- cpuinfo.set_cpu_model(0, cpumodel);
- // Assume single CPU in bare metal mode. Just read the ID register and feature bits directly.
- uint64_t fr0, pfr0, midr;
- __asm __volatile(
- "MRS %0, ID_AA64ISAR0_EL1\n"
- "MRS %1, ID_AA64PFR0_EL1\n"
- "MRS %2, midr_el1"
- : "=r"(fr0), "=r"(pfr0), "=r"(midr));
- if((fr0 >> 44) & 0xf)
- {
- cpuinfo.set_dotprod(true);
- }
- if((pfr0 >> 16) & 0xf)
- {
- cpuinfo.set_fp16(true);
- }
- if((pfr0 >> 32) & 0xf)
- {
- cpuinfo.set_sve(true);
- }
-#else /* #elif(BARE_METAL) && defined(__aarch64__) */
- ARM_COMPUTE_UNUSED(cpuinfo);
-#endif /* !defined(BARE_METAL) && !defined(__APPLE__) && (defined(__arm__) || defined(__aarch64__)) */
-}
-
-unsigned int get_threads_hint()
-{
- unsigned int num_threads_hint = 1;
-
-#if !defined(BARE_METAL)
- std::map<std::string, unsigned int> cpu_part_occurrence_map;
-
- // CPU part regex
- regex_t cpu_part_rgx;
- memset(&cpu_part_rgx, 0, sizeof(regex_t));
- int ret_status = regcomp(&cpu_part_rgx, R"(.*CPU part.+/?\:[[:space:]]+([[:alnum:]]+).*)", REG_EXTENDED);
- ARM_COMPUTE_UNUSED(ret_status);
- ARM_COMPUTE_ERROR_ON_MSG(ret_status != 0, "Regex compilation failed.");
-
- // Read cpuinfo and get occurrence of each core
- std::ifstream cpuinfo;
- cpuinfo.open("/proc/cpuinfo", std::ios::in);
- if(cpuinfo.is_open())
- {
- std::string line;
- while(bool(getline(cpuinfo, line)))
- {
- std::array<regmatch_t, 2> match;
- ret_status = regexec(&cpu_part_rgx, line.c_str(), 2, match.data(), 0);
- if(ret_status == 0)
- {
- std::string cpu_part = line.substr(match[1].rm_so, (match[1].rm_eo - match[1].rm_so));
- if(cpu_part_occurrence_map.find(cpu_part) != cpu_part_occurrence_map.end())
- {
- cpu_part_occurrence_map[cpu_part]++;
- }
- else
- {
- cpu_part_occurrence_map[cpu_part] = 1;
- }
- }
- }
- }
- regfree(&cpu_part_rgx);
-
- // Get min number of threads
- auto min_common_cores = std::min_element(cpu_part_occurrence_map.begin(), cpu_part_occurrence_map.end(),
- [](const std::pair<std::string, unsigned int> &p1, const std::pair<std::string, unsigned int> &p2)
- {
- return p1.second < p2.second;
- });
-
- // Set thread hint
- num_threads_hint = cpu_part_occurrence_map.empty() ? std::thread::hardware_concurrency() : min_common_cores->second;
-#endif /* !defined(BARE_METAL) */
-
- return num_threads_hint;
-}
-} // namespace cpu
-} // namespace utils
-} // namespace arm_compute
diff --git a/src/runtime/CPUUtils.h b/src/runtime/CPUUtils.h
deleted file mode 100644
index 452d3d58ca..0000000000
--- a/src/runtime/CPUUtils.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2018-2020 Arm Limited.
- *
- * SPDX-License-Identifier: MIT
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-#ifndef ARM_COMPUTE_RUNTIME_CPU_UTILS_H
-#define ARM_COMPUTE_RUNTIME_CPU_UTILS_H
-
-namespace arm_compute
-{
-class CPUInfo;
-
-namespace utils
-{
-namespace cpu
-{
-/** This function will try to detect the CPU configuration on the system and will fill
- * the cpuinfo object accordingly to reflect this.
- *
- * @param[out] cpuinfo @ref CPUInfo to be used to hold the system's cpu configuration.
- */
-void get_cpu_configuration(CPUInfo &cpuinfo);
-/** Some systems have both big and small cores, this fuction computes the minimum number of cores
- * that are exactly the same on the system. To maximize performance the library attempts to process
- * workloads concurrently using as many threads as big cores are available on the system.
- *
- * @return The minumum number of common cores.
- */
-unsigned int get_threads_hint();
-} // namespace cpu
-} // namespace utils
-} // namespace arm_compute
-#endif /* ARM_COMPUTE_RUNTIME_CPU_UTILS_H */
diff --git a/src/runtime/IScheduler.cpp b/src/runtime/IScheduler.cpp
index eae34b98eb..df04fed401 100644
--- a/src/runtime/IScheduler.cpp
+++ b/src/runtime/IScheduler.cpp
@@ -26,7 +26,7 @@
#include "arm_compute/core/CPP/ICPPKernel.h"
#include "arm_compute/core/Error.h"
#include "arm_compute/core/Window.h"
-#include "src/runtime/CPUUtils.h"
+#include "src/common/cpuinfo/CpuInfo.h"
#include "src/runtime/SchedulerUtils.h"
namespace arm_compute
@@ -34,9 +34,8 @@ namespace arm_compute
IScheduler::IScheduler()
: _cpu_info()
{
- utils::cpu::get_cpu_configuration(_cpu_info);
// Work out the best possible number of execution threads
- _num_threads_hint = utils::cpu::get_threads_hint();
+ _num_threads_hint = cpuinfo::num_threads_hint();
}
CPUInfo &IScheduler::cpu_info()
diff --git a/src/runtime/MEMUtils.cpp b/src/runtime/MEMUtils.cpp
deleted file mode 100644
index 8b39a0f3f4..0000000000
--- a/src/runtime/MEMUtils.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2018-2020 Arm Limited.
- *
- * SPDX-License-Identifier: MIT
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-#include "arm_compute/core/CPP/CPPTypes.h"
-#include "arm_compute/core/Error.h"
-#include "support/StringSupport.h"
-
-#ifndef BARE_METAL
-#include <fstream>
-#include <iterator>
-#include <sstream>
-#endif // ifndef BARE_METAL
-
-namespace
-{
-void parse_mem_info(size_t &total, size_t &free, size_t &buffer)
-{
- free = 0;
- total = 0;
- buffer = 0;
-#ifndef BARE_METAL
- size_t memcache = 0;
- size_t memfree = 0;
- std::ifstream meminfo_f;
- meminfo_f.open("/proc/meminfo", std::ios::in);
-
- if(meminfo_f.is_open())
- {
- std::string line;
- while(bool(getline(meminfo_f, line)))
- {
- std::istringstream iss(line);
- std::vector<std::string> tokens((std::istream_iterator<std::string>(iss)),
- std::istream_iterator<std::string>());
- if(tokens[0] == "MemTotal:")
- {
- total = arm_compute::support::cpp11::stoul(tokens[1], nullptr);
- }
- else if(tokens[0] == "MemFree:")
- {
- memfree = arm_compute::support::cpp11::stoul(tokens[1], nullptr);
- }
- else if(tokens[0] == "Buffers:")
- {
- buffer = arm_compute::support::cpp11::stoul(tokens[1], nullptr);
- }
- else if(tokens[0] == "Cached:")
- {
- memcache = arm_compute::support::cpp11::stoul(tokens[1], nullptr);
- }
- }
- free = memfree + (buffer + memcache);
- }
-#endif // ifndef BARE_METAL
-}
-
-} // namespace
-
-namespace arm_compute
-{
-void MEMInfo::set_policy(MemoryPolicy policy)
-{
- _policy = policy;
-}
-
-MemoryPolicy MEMInfo::get_policy()
-{
- return _policy;
-}
-MemoryPolicy MEMInfo::_policy = { MemoryPolicy::NORMAL };
-
-MEMInfo::MEMInfo()
- : _total(0), _free(0), _buffer(0)
-{
- parse_mem_info(_total, _free, _buffer);
-}
-
-size_t MEMInfo::get_total_in_kb() const
-{
- return _total;
-}
-
-} // namespace arm_compute
diff --git a/src/runtime/OMP/OMPScheduler.cpp b/src/runtime/OMP/OMPScheduler.cpp
index a8bd5a0d60..ca763f907b 100644
--- a/src/runtime/OMP/OMPScheduler.cpp
+++ b/src/runtime/OMP/OMPScheduler.cpp
@@ -27,7 +27,6 @@
#include "arm_compute/core/Error.h"
#include "arm_compute/core/Helpers.h"
#include "arm_compute/core/Utils.h"
-#include "src/runtime/CPUUtils.h"
#include <omp.h>
namespace arm_compute
diff --git a/src/runtime/RuntimeContext.cpp b/src/runtime/RuntimeContext.cpp
index 504a74c949..d1dea066e7 100644
--- a/src/runtime/RuntimeContext.cpp
+++ b/src/runtime/RuntimeContext.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 Arm Limited.
+ * Copyright (c) 2019, 2021 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -29,7 +29,7 @@
namespace arm_compute
{
RuntimeContext::RuntimeContext()
- : _owned_scheduler(SchedulerFactory::create()), _scheduler(_owned_scheduler.get()), _device_props()
+ : _owned_scheduler(SchedulerFactory::create()), _scheduler(_owned_scheduler.get())
{
}
@@ -48,9 +48,4 @@ IAssetManager *RuntimeContext::asset_manager()
{
return nullptr;
}
-
-const DeviceProperties &RuntimeContext::properties()
-{
- return _device_props;
-}
} // namespace arm_compute
diff --git a/tests/main.cpp b/tests/main.cpp
index 308338e781..e1963b1609 100644
--- a/tests/main.cpp
+++ b/tests/main.cpp
@@ -45,6 +45,7 @@
#include "utils/TypePrinter.h"
#endif /* ARM_COMPUTE_CL */
#include "arm_compute/runtime/Scheduler.h"
+#include "src/common/cpuinfo/CpuModel.h"
#include <fstream>
#include <initializer_list>
@@ -235,13 +236,15 @@ int main(int argc, char **argv)
#endif /* ARM_COMPUTE_CL */
const arm_compute::CPUInfo &cpu_info = Scheduler::get().cpu_info();
const unsigned int num_cpus = cpu_info.get_cpu_num();
+ p->print_entry("cpu_has_sve", support::cpp11::to_string(cpu_info.has_sve()));
p->print_entry("cpu_has_fp16", support::cpp11::to_string(cpu_info.has_fp16()));
+ p->print_entry("cpu_has_bf16", support::cpp11::to_string(cpu_info.has_bf16()));
p->print_entry("cpu_has_dotprod", support::cpp11::to_string(cpu_info.has_dotprod()));
for(unsigned int j = 0; j < num_cpus; ++j)
{
const CPUModel model = cpu_info.get_cpu_model(j);
- p->print_entry("CPU" + support::cpp11::to_string(j), cpu_model_to_string(model));
+ p->print_entry("CPU" + support::cpp11::to_string(j), cpuinfo::cpu_model_to_string(model));
}
p->print_entry("Iterations", support::cpp11::to_string(options.iterations->value()));
p->print_entry("Threads", support::cpp11::to_string(threads->value()));
diff --git a/utils/TypePrinter.h b/utils/TypePrinter.h
index e849edc092..e8cb6e85b7 100644
--- a/utils/TypePrinter.h
+++ b/utils/TypePrinter.h
@@ -24,7 +24,6 @@
#ifndef __ARM_COMPUTE_TYPE_PRINTER_H__
#define __ARM_COMPUTE_TYPE_PRINTER_H__
-#include "arm_compute/core/CPP/CPPTypes.h"
#include "arm_compute/core/Dimensions.h"
#include "arm_compute/core/Error.h"
#include "arm_compute/core/GPUTarget.h"
@@ -2014,60 +2013,6 @@ inline std::string to_string(const DetectionWindow &detection_window)
return str.str();
}
-/** Formatted output of the CPUModel type.
- *
- * @param[out] os Output stream
- * @param[in] cpu_model Model to output
- *
- * @return Modified output stream.
- */
-inline ::std::ostream &operator<<(::std::ostream &os, const CPUModel &cpu_model)
-{
- switch(cpu_model)
- {
- case CPUModel::GENERIC:
- os << "GENERIC";
- break;
- case CPUModel::GENERIC_FP16:
- os << "GENERIC_FP16";
- break;
- case CPUModel::GENERIC_FP16_DOT:
- os << "GENERIC_FP16_DOT";
- break;
- case CPUModel::A53:
- os << "A53";
- break;
- case CPUModel::A55r0:
- os << "A55r0";
- break;
- case CPUModel::A55r1:
- os << "A55r1";
- break;
- case CPUModel::A73:
- os << "A73";
- break;
- case CPUModel::X1:
- os << "X1";
- break;
- default:
- ARM_COMPUTE_ERROR("NOT_SUPPORTED!");
- }
-
- return os;
-}
-
-/** Formatted output of the CPUModel type.
- *
- * @param[in] cpu_model Model to output
- *
- * @return Formatted string.
- */
-inline std::string to_string(const CPUModel &cpu_model)
-{
- std::stringstream str;
- str << cpu_model;
- return str.str();
-}
/** Formatted output of a vector of objects.
*
* @param[out] os Output stream