在具有异构 CPU 架构的设备上检测是否 运行
Detect if running on a device with heterogeneous CPU architecture
我对这个非常具体。
我需要知道该设备是否具有 CPU,它具有像 ARM's big.LITTLE technology 这样的异构内核,例如,一组 4 个 ARM Cortex-A53 + 另一组 4 个更强大的 ARM Cortex-A72,总共 8 个核心,基本上是同一芯片中的 2 个处理器。处理器型号并不重要。
我正在考虑的是读取所有内核的 scaling_max_freq
并将具有不同最大频率的内核分组(然后比较它们)但我注意到在某些设备中,通往任何内核的路径不是 cpu0其实是symlink到/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
也就是说,如果我尝试读取 cpu3 的 scaling_max_freq,它将是一个 link 到 cpu0 的 scaling_max_freq。我想知道在这种情况下我是否可以认为我们不是 运行 异类。
CPU class
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 class
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;
}
}
您可以解析 $ cat /proc/cpuinfo
的结果,如 "model name":
中所述
processor : 0
[...]
model name : Intel(R) Core(TM) i5 CPU M 560 @ 2.67GHz
[...]
processor : 1
[...]
请注意,"cpu MHz"描述的是当前频率,而不是最大值。
参考文献:
https://unix.stackexchange.com/questions/146051/number-of-processors-in-proc-cpuinfo
编辑:如果您的 OS 没有 return 型号名称,BogoMips 可以用作比较单位(尽管 Wikipedia 在下面的描述中不推荐).至少你可以用它来识别你有一个异构架构。
BogoMips (from "bogus" and MIPS) is an unscientific measurement of CPU
speed made by the Linux kernel when it boots to calibrate an internal
busy-loop. An often-quoted definition of the term is "the number of
million times per second a processor can do absolutely nothing".
BogoMips is a value that can be used to verify whether the processor
in question is in the proper range of similar processors, i.e.
BogoMips represents a processor's clock frequency as well as the
potentially present CPU cache. It is not usable for performance
comparisons among different CPUs.
Here 您可以找到完整的 BogoMips 评分列表。
这是我目前在 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
}
我对这个非常具体。 我需要知道该设备是否具有 CPU,它具有像 ARM's big.LITTLE technology 这样的异构内核,例如,一组 4 个 ARM Cortex-A53 + 另一组 4 个更强大的 ARM Cortex-A72,总共 8 个核心,基本上是同一芯片中的 2 个处理器。处理器型号并不重要。
我正在考虑的是读取所有内核的 scaling_max_freq
并将具有不同最大频率的内核分组(然后比较它们)但我注意到在某些设备中,通往任何内核的路径不是 cpu0其实是symlink到/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
也就是说,如果我尝试读取 cpu3 的 scaling_max_freq,它将是一个 link 到 cpu0 的 scaling_max_freq。我想知道在这种情况下我是否可以认为我们不是 运行 异类。
CPU class
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 class
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;
}
}
您可以解析 $ cat /proc/cpuinfo
的结果,如 "model name":
processor : 0
[...]
model name : Intel(R) Core(TM) i5 CPU M 560 @ 2.67GHz
[...]
processor : 1
[...]
请注意,"cpu MHz"描述的是当前频率,而不是最大值。
参考文献:
https://unix.stackexchange.com/questions/146051/number-of-processors-in-proc-cpuinfo
编辑:如果您的 OS 没有 return 型号名称,BogoMips 可以用作比较单位(尽管 Wikipedia 在下面的描述中不推荐).至少你可以用它来识别你有一个异构架构。
BogoMips (from "bogus" and MIPS) is an unscientific measurement of CPU speed made by the Linux kernel when it boots to calibrate an internal busy-loop. An often-quoted definition of the term is "the number of million times per second a processor can do absolutely nothing".
BogoMips is a value that can be used to verify whether the processor in question is in the proper range of similar processors, i.e. BogoMips represents a processor's clock frequency as well as the potentially present CPU cache. It is not usable for performance comparisons among different CPUs.
Here 您可以找到完整的 BogoMips 评分列表。
这是我目前在 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
}