android - 检测是否在具有异构 CPU 架构的设备上运行
问题描述
我对这一点非常具体。我需要知道该设备是否有一个 CPU 具有像ARM 的 big.LITTLE 技术这样的异构内核,例如,一组 4 个 ARM Cortex-A53 + 另一组 4 个更强大的 ARM Cortex-A72,总共 8 个内核,基本上 2处理器在同一芯片中。处理器型号并不重要。
我正在考虑的是读取scaling_max_freq
所有内核并将具有不同最大频率的内核分组(然后比较它们),但我注意到在某些设备中,任何不是cpu0的内核的路径实际上是 /sys/devices/ 的符号链接系统/cpu/ cpu0 /cpufreq/scaling_max_freq
也就是说,如果我尝试读取 cpu3 的 scaling_max_freq,它将是一个指向 cpu0 的 scaling_max_freq 的链接。我想知道在这种情况下我是否可以认为我们不是在异构环境中运行。
CPU类
public final class CPU {
// To be formatted with specific core number
private static final String CPU_DIR = "/sys/devices/system/cpu/cpu%d";
private static final String CPUFREQ_DIR = CPU_DIR + "/cpufreq";
public static final String SCALING_MAX_FREQ = CPUFREQ_DIR + "/scaling_max_freq";
private static final String DEFAULT_FREQS = "200000 400000 800000 1200000";
private CPU() {
}
// Here I'd replace 0 with (other) core number
@NonNull
public static synchronized String[] getAvailFreqs() {
String[] split;
String freqs = FileUtils.readFile(format(SCALING_AVAIL_FREQS, 0), DEFAULT_FREQS);
try {
split = freqs.split(" ");
} catch (Exception e) {
split = DEFAULT_FREQS.split(" ");
}
return split;
}
// Here I'd replace 0 with (other) core number
public static synchronized int getMaxFreq() {
try {
return Integer.parseInt(FileUtils.readFile(format(SCALING_MAX_FREQ, 0), "1200000"));
} catch (Exception ignored){}
return 1200000;
}
private static String format(String format, Object arg) {
return String.format(Locale.US, format, arg);
}
}
FileUtils 类
public final class FileUtils {
private FileUtils() {
}
public static String readFile(String pathname, String defaultOutput) {
return baseReadSingleLineFile(new File(pathname), defaultOutput);
}
public static String readFile(File file, String defaultOutput) {
return baseReadSingleLineFile(file, defaultOutput);
}
// Async
private static String baseReadSingleLineFile(File file, String defaultOutput) {
String ret = defaultOutput;
Thread thread = new Thread(() -> {
if (file.isFile() || file.exists()) {
if (file.canRead()) {
try {
FileInputStream inputStream = new FileInputStream(file);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line = reader.readLine(); // Fisrt line
reader.close();
inputStream.close();
ret = line;
} catch (Exception ignored) {}
} else
// Uses cat command
ret = RootUtils.readFile(file, defaultOutput);
}
});
thread.start();
// 3 seconds timeout
long endTimeMillis = System.currentTimeMillis() + 3000;
while (thread.isAlive())
if (System.currentTimeMillis() > endTimeMillis)
return defaultOutput;
return ret;
}
}
解决方案
这是我目前在 Kotlin 中的方法:
class CpuManager {
// GOTO: val clusters: List<CpuCluster>
companion object {
private const val CPU_DIR = "/sys/devices/system/cpu/cpu%d"
private const val CPUFREQ_DIR = "$CPU_DIR/cpufreq"
const val SCALING_CUR_FREQ = "$CPUFREQ_DIR/scaling_cur_freq"
const val SCALING_MAX_FREQ = "$CPUFREQ_DIR/scaling_max_freq"
const val SCALING_MIN_FREQ = "$CPUFREQ_DIR/scaling_min_freq"
const val SCALING_AVAIL_FREQS = "$CPUFREQ_DIR/scaling_available_frequencies"
private const val DEFAULT_FREQS = "200000 400000 800000 1200000"
}
private fun getAvailFreqs(cpuCore: Int = 0) : Array<String> {
val freqs = FileUtils.readFile(format(SCALING_AVAIL_FREQS, cpuCore), DEFAULT_FREQS)
return try {
freqs.split(" ").dropLastWhile { it.isEmpty() }.toTypedArray()
} catch (e: Exception) {
DEFAULT_FREQS.split(" ").dropLastWhile { it.isEmpty() }.toTypedArray()
}
}
@JvmOverloads
fun getMaxFreq(cpuCore: Int = 0): Int {
return try {
FileUtils.readFile(format(SCALING_MAX_FREQ, cpuCore), "1200000").toInt()
} catch (ignored: Exception) {
1200000
}
}
private fun format(format: String, arg: Any): String {
return String.format(Locale.US, format, arg)
}
val cpuCount: Int
get() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return Runtime.getRuntime().availableProcessors()
}
class CpuFilter : FileFilter {
override fun accept(pathname: File): Boolean {
return Pattern.matches("cpu[0-11]+", pathname.name)
}
}
return try {
val dir = File("/sys/devices/system/cpu/")
val files = dir.listFiles(CpuFilter())
files.size
} catch (e: Exception) {
1
}
}
val clusters: List<CpuCluster>
get() {
val cpuCount = this.cpuCount
val clustersList = mutableListOf<CpuCluster>()
val cpuCores = mutableListOf<CpuCore>()
for (i in (0 until cpuCount)) {
val cpuCore = CpuCore(coreNum = i)
cpuCore.availFreqs = getAvailFreqs(i)
//cpuCore.availGovs = getAvailGovs(i)
//cpuCore.governorTunables = getGovernorTunables(cpuCore.currentGov, cpuCore.coreNum)
cpuCores.add(cpuCore)
}
val allFreqs = mutableListOf<Array<Int>>()
for (cpu in 0 until cpuCount) {
val availCpuFreqs = cpuCores[cpu].availFreqs.toIntArray() // Extension function below
availCpuFreqs.sortWith(Comparator { o1, o2 -> o1.compareTo(o2) })
allFreqs.add(availCpuFreqs)
}
val maxFreqs = mutableListOf<Int>()
allFreqs.forEach { freqList ->
val coreMax = freqList[freqList.size - 1]
maxFreqs.add(coreMax)
}
val firstMaxFreq = allFreqs.first().last()
// Here is the logic I suggested
val distinctMaxFreqs = mutableListOf<Int>()
distinctMaxFreqs.add(firstMaxFreq)
maxFreqs.forEach {
if (it != firstMaxFreq && !distinctMaxFreqs.contains(it)) {
distinctMaxFreqs.add(it)
}
}
val clustersCount = distinctMaxFreqs.size
if (clustersCount == 1) {
clustersList.add(CpuCluster(cpuCores))
} else {
distinctMaxFreqs.forEach { maxFreq ->
val cpuClusterCores = mutableListOf<CpuCore>()
cpuCores.forEach {
if (it.maxFreq == maxFreq) {
cpuClusterCores.add(it)
}
}
clustersList.add(CpuCluster(cpuClusterCores))
}
}
return clustersList
}
data class CpuCluster(val cpuCores: List<CpuCore>) {
val totalCpuCount: Int
get() {
return cpuCores.size
}
val range: IntRange
get() {
return if (cpuCores.isNullOrEmpty()) {
0..0
} else {
IntRange(cpuCores.first().coreNum, cpuCores.last().coreNum)
}
}
}
data class CpuCore(val coreNum: Int = 0, var availFreqs: Array<String> = DEFAULT_FREQS.split(" ").toTypedArray(), var availGovs: Array<String> = DEFAULT_GOVERNORS.split(" ").toTypedArray()) {
var governorTunables: ArrayList<GovernorTunable>? = null
val currentGov: String
get() {
return FileUtils.readFile(
"/sys/devices/system/cpu/cpu$coreNum/cpufreq/scaling_governor",
"interactive")
}
val currentFreq: Int
get() {
return try {
FileUtils.readFile(
"/sys/devices/system/cpu/cpu$coreNum/cpufreq/scaling_cur_freq",
"800000")
.toInt()
} catch (e: Exception) { 800000 }
}
val minFreq: Int
get() {
return try {
availFreqs.sortWith(java.util.Comparator { o1, o2 -> o1.toInt().compareTo(o2.toInt()) })
availFreqs.first().toInt()
} catch (e: Exception) { 400000 }
}
val maxFreq: Int
get() {
return try {
availFreqs.sortWith(java.util.Comparator { o1, o2 -> o1.toInt().compareTo(o2.toInt()) })
availFreqs.last().toInt()
} catch (e: Exception) { 800000 }
}
}
}
private fun Array<String>.toIntArray(): Array<Int> {
val list = mutableListOf<Int>()
this.forEach { list.add(it.toInt()) }
return list.toTypedArray()
}
所以现在我可以:
val cpuManager = CpuManager()
val clusters: List<CpuCluster> = cpuManager.clusters
if (clusters.size > 1) {
// Heterogeneous computing architecture
}
推荐阅读
- python - 如何将 tf.Variable 转换为 numpy?
- matlab - 使用按钮单击中断循环 Appdesigner GUI [在调试模式下工作但不在正常模式下工作] [绘图和按钮都具有不同的功能]
- python - 在python中获取字符串值的正则表达式
- python - 如何将 Tkinter 8.6.8 升级到 8.6.9?
- reactjs - React route:如何从组件中获取当前的route key
- javascript - 即使窗口屏幕大小为
- c# - 从 .Net Core 中的启动配置方法调用控制器操作
- iso - 为 post-fd 操作系统(例如 CentOS 8)生成 kickstart ISO 并在打包程序 vsphere-iso 调用中使用它
- laravel - Laravel 8x 使用 JetStream Teams 创建用户表
- jupyter-notebook - 获取:启动 Jupyter 时“无法获取元数据”