Определить, работает ли на устройстве с гетерогенной архитектурой процессора - PullRequest
0 голосов
/ 26 августа 2018

Я очень конкретен в этом. Мне нужно знать, если устройство имеет процессор, который имеет гетерогенные ядра, такие как технология ARM big.LITTLE , например, набор из 4 ARM Cortex-A53 + еще один набор из 4 более мощных ARM Cortex-A72, всего 8 ядер, в основном 2 процессора в одном чипе. Модель процессоров на самом деле не имеет значения.

Что я рассматриваю, так это прочитать scaling_max_freq всех ядер и сгруппировать их с разными максимальными частотами (а затем сравнить их), но я заметил, что в некоторых устройствах путь к любому ядру не cpu0 фактически является символической ссылкой на / sys / devices / system / cpu / cpu0 / cpufreq / scaling_max_freq

То есть, если я попытаюсь прочитать scaling_max_freq cpu3, ​​это будет ссылка на scaling_max_freq cpu0. Интересно, могу ли я считать, что в этом случае мы не работаем в гетерогенной среде?

класс процессора

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;
    }
}

Ответы [ 2 ]

0 голосов
/ 08 декабря 2018

Вот мой текущий подход в Котлине:

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 
}
0 голосов
/ 04 сентября 2018

Вы можете проанализировать результаты из $ 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 можно использовать в качестве единицы сравнения (хотяэто не рекомендуется Википедия в описании ниже).По крайней мере, вы можете использовать его, чтобы определить, что у вас гетерогенная архитектура.

BogoMips (из "фальшивых" и MIPS) - это ненаучное измерение скорости процессора, выполняемое ядром Linux при загрузке для калибровкивнутренняя занятая петля.Часто цитируемое определение термина «число миллионов раз в секунду, когда процессор может абсолютно ничего не делать».

BogoMips - это значение, которое можно использовать для проверки того, находится ли рассматриваемый процессор в надлежащемдиапазон схожих процессоров, т.е. BogoMips представляет тактовую частоту процессора, а также потенциально присутствующий кэш процессора.Он не может использоваться для сравнения производительности разных процессоров.

Здесь вы можете найти полный список оценок BogoMips.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...