【问题标题】:Detect if running on a device with heterogeneous CPU architecture检测是否在具有异构 CPU 架构的设备上运行
【发布时间】:2018-08-25 22:23:35
【问题描述】:

我对此非常具体。 我需要知道该设备是否具有具有异构内核的 CPU,例如 ARM's big.LITTLE technology,例如,一组 4 个 ARM Cortex-A53 + 另一组 4 个更强大的 ARM Cortex-A72,总共 8 个内核,基本上 2 个处理器同一个芯片。处理器型号并不重要。

我正在考虑的是读取所有内核的scaling_max_freq 并将具有不同最大频率的内核分组(然后比较它们),但我注意到在某些设备中,任何不是 cpu0 实际上是 /sys/devices/system/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;
    }
}

【问题讨论】:

    标签: android linux linux-kernel cpu root


    【解决方案1】:

    您可以解析来自$ cat /proc/cpuinfo 的结果,如“模型名称”中所述:

    processor   : 0
    [...]
    model name  : Intel(R) Core(TM) i5 CPU       M 560  @ 2.67GHz
    [...]
    
    processor   : 1
    [...]
    

    请注意,“cpu MHz”描述的是当前频率,而不是最大值。

    参考资料:

    https://unix.stackexchange.com/questions/87522/why-do-cpuinfo-cur-freq-and-proc-cpuinfo-report-different-numbers

    https://unix.stackexchange.com/questions/146051/number-of-processors-in-proc-cpuinfo

    编辑:如果您的操作系统不返回型号名称,BogoMips 可以用作比较单元(尽管在下面的描述中 Wikipedia 不推荐它)。至少您可以使用它来识别您拥有异构架构。

    BogoMips(来自“bogus”和 MIPS)是对 CPU 的不科学测量 Linux 内核在启动以校准内部 忙循环。该术语的一个经常被引用的定义是“ 每秒百万次,处理器绝对无能为力”。

    BogoMips 是一个值,可以用来验证处理器是否 有问题的是在类似处理器的适当范围内,即 BogoMips 代表处理器的时钟频率以及 可能存在的 CPU 缓存。它不能用于性能 不同 CPU 之间的比较。

    Here你可以找到完整的 BogoMips 评级列表。

    【讨论】:

    • 遗憾的是,cat /proc/cpuinfo 的 Android 输出不包含此类信息
    • 如下所示:imgur.com/a/iEPvmUW(输出的最后一行显示Hardware: Qualcomm Technologies Inc, MSM8953
    • @LennoardSilva,$ lscpu 怎么样?
    • @LennoardSilva,我刚刚编辑了关于 BogoMips 的答案。如果您用完了选项,它可能会很有用。
    • lscpu 在 Android 默认二进制文件中不可用。我也尝试过busybox,但没有运气。我想我已经没有时间和选择了。我会奖励你的。
    【解决方案2】:

    这是我目前在 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 
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-20
      • 2010-09-14
      • 2018-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多