【Android】强制使用 CPU 大核或超大核
摘要
-
说明:通过
JNI
在应用层读取/sys/devices/system/cpu
下各核的cpuinfo_max_freq
,选出频率最高的核心集合,随机挑选一个并尝试把主线程绑定到该核,以期望获得更稳定或更高的主线程性能。 -
目的:在需要主线程更稳定的短时间高负载场景(如关键 UI 动画或延迟敏感计算)尝试降低抖动。
实现要点
-
native 侧实现三个 JNI 方法:
getMaxFreqCores
、bindThreadToCore
、getCurrentCpu
;应用层调用这些方法决定是否绑定主线程。 -
getMaxFreqCores 通过读取每个
cpuN/cpufreq/cpuinfo_max_freq
找出最大频率并返回所有匹配核心索引。 -
bindThreadToCore 用
sched_setaffinity
将当前线程绑定到指定 CPU。 -
getCurrentCpu 用
sched_getcpu
返回当前线程运行的 CPU 编号,用于验证绑定是否生效。
示例代码
JNI:
#include <jni.h>
#include <string>
#include <unistd.h>extern "C"
JNIEXPORT jintArray JNICALL
Java_XXX_getMaxFreqCores(JNIEnv *env, jobject /* this */) {char path[128];FILE *fp;int maxFreq = -1;// 获取CPU核心数int cpuCount = sysconf(_SC_NPROCESSORS_ONLN);int cores[cpuCount];int coreCount = 0;// 第一次遍历,找最大频率for (int i = 0; i < cpuCount; i++) {snprintf(path, sizeof(path),"/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", i);fp = fopen(path, "r");if (fp) {int freq = 0;if (fscanf(fp, "%d", &freq) == 1) {if (freq > maxFreq) {maxFreq = freq;}}fclose(fp);} else {break; // 核心不存在}}// 第二次遍历,把所有等于 maxFreq 的核心加入数组for (int i = 0; i < cpuCount; i++) {snprintf(path, sizeof(path),"/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", i);fp = fopen(path, "r");if (fp) {int freq = 0;if (fscanf(fp, "%d", &freq) == 1) {if (freq == maxFreq) {cores[coreCount++] = i;}}fclose(fp);} else {break;}}// 返回期望的结果jintArray result = env->NewIntArray(coreCount);if (result == nullptr) return nullptr;env->SetIntArrayRegion(result, 0, coreCount, cores);return result;
}extern "C"
JNIEXPORT jint JNICALL
Java_XXX_bindThreadToCore(JNIEnv *env, jobject /* this */, jint core) {cpu_set_t set;CPU_ZERO(&set);CPU_SET(core, &set);pid_t tid = gettid();int result = sched_setaffinity(tid, sizeof(set), &set);return result; // 0 = 成功, -1 = 失败
}extern "C"
JNIEXPORT jint JNICALL
Java_XXX_getCurrentCpu(JNIEnv *env, jobject /* this */) {return sched_getcpu();
}
应用层调用:
class MainActivity : AppCompatActivity() {companion object {init {System.loadLibrary("jni")}}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val maxFreqCores = getMaxFreqCores()maxFreqCores.forEach { core ->Log.d("Tyhoo", "最高频率核心: cpu$core")}val result = isCurrentInMaxFreqCore(maxFreqCores)if (!result) {// 随机选择一个最高频率核心val randomMaxFreqCore = maxFreqCores.random()Log.d("Tyhoo", "将主线程绑定到最高频率核心 cpu$randomMaxFreqCore")val ret = bindThreadToCore(randomMaxFreqCore)if (ret == 0) {Log.d("Tyhoo", "主线程已绑定到 cpu$randomMaxFreqCore")isCurrentInMaxFreqCore(maxFreqCores)} else {Log.d("Tyhoo", "绑定失败,错误码: $ret")}}}private fun isCurrentInMaxFreqCore(cores: IntArray): Boolean {val currentCpu = getCurrentCpu()if (cores.contains(currentCpu)) {Log.d("Tyhoo", "主线程正在运行在最高频率核心 cpu$currentCpu")return true} else {Log.d("Tyhoo", "主线程在 cpu$currentCpu, 不是最高频率核心 ${cores.joinToString(", ") { "cpu$it" }}")return false}}external fun getMaxFreqCores(): IntArrayexternal fun bindThreadToCore(core: Int): Intexternal fun getCurrentCpu(): Int
}
打印日志:
com.tyhoo.example.jni D 最高频率核心: cpu0
com.tyhoo.example.jni D 最高频率核心: cpu1
com.tyhoo.example.jni D 最高频率核心: cpu2
com.tyhoo.example.jni D 最高频率核心: cpu3
com.tyhoo.example.jni D 主线程在 cpu5, 不是最高频率核心 cpu0, cpu1, cpu2, cpu3
com.tyhoo.example.jni D 将主线程绑定到最高频率核心 cpu1
com.tyhoo.example.jni D 主线程已绑定到 cpu1
com.tyhoo.example.jni D 主线程正在运行在最高频率核心 cpu1
结论
该方案实现简单、不改动架构,适合做短期实验和定位性能抖动。但生产环境使用前必须做好兼容性、稳健性与性能回归测试,避免因绑定主线程带来意外副作用。