Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Android 强制使用 CPU 大核或超大核 #64

@cnwutianhao

Description

@cnwutianhao

摘要

  • 说明:通过 JNI 在应用层读取 /sys/devices/system/cpu 下各核的 cpuinfo_max_freq,选出频率最高的核心集合,随机挑选一个并尝试把主线程绑定到该核,以期望获得更稳定或更高的主线程性能。

  • 目的:在需要主线程更稳定的短时间高负载场景(如关键 UI 动画或延迟敏感计算)尝试降低抖动。

实现要点

  • native 侧实现三个 JNI 方法:getMaxFreqCoresbindThreadToCoregetCurrentCpu;应用层调用这些方法决定是否绑定主线程。

  • 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(): IntArray

    external fun bindThreadToCore(core: Int): Int

    external 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

结论

该方案实现简单、不改动架构,适合做短期实验和定位性能抖动。但生产环境使用前必须做好兼容性、稳健性与性能回归测试,避免因绑定主线程带来意外副作用。

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions