CPU調(diào)頻、線程綁核、優(yōu)先級控制實踐
0、背景
為了進一步優(yōu)化App性能,最近針對如何提高應(yīng)用對CPU的資源使用、以及在多線程環(huán)境下如何提高關(guān)鍵線程的執(zhí)行優(yōu)先級做了技術(shù)調(diào)研。本文是對技術(shù)調(diào)研過程的階段性總結(jié),將分別介紹普通應(yīng)用如何調(diào)控App頻率、如何將指定線程綁定到特定CPU、如何通過提升線程優(yōu)先級獲得更多CPU時間片。
1、CPU調(diào)頻
1.1 概念
通常更高的CPU頻率代表了更快的運行速度,一個設(shè)備可能包含多個CPU,以我目前使用的Mi 11 Pro為例,它的CPU為8核分別為,1 x 2.84GHz (ARM 最新Cortex X1 核心)+3 x 2.4GHz (Cortex A78)+4 x 1.8GHz (Cortex A55) 。 這里列出的CPU頻率為CPU物理理論上的最大頻率,在實際運行過程中CPU的頻率范圍為governor動態(tài)控制的。目前的Androd設(shè)備普遍采用schedutil gover進行調(diào)頻控制,它會根據(jù)運行過程的CPU負載進行調(diào)頻,不過默認的調(diào)頻存在一些限制,比如調(diào)頻之間的間隔需>10ms, 并且根據(jù)schedutil的升頻計算公式,并不保證能直接升頻到最高頻率。
在實際應(yīng)用中,如果我們已經(jīng)知道接下來需要執(zhí)行高CPU負載任務(wù),通過提前主動升頻來提升性能,就能減少卡頓或者提高任務(wù)的執(zhí)行耗時。
在Android系統(tǒng) :可以通過
echo [頻率]>/sys/devices/system/cpu/cpu*/cpufreq/scaling set speed
來修改目標CPU的頻率,但這需要root權(quán)限才能執(zhí)行。對于普遍的應(yīng)用程序,經(jīng)過調(diào)研發(fā)現(xiàn),高通提供了一套針對高通芯片的性能Jon告知SDKPower,利用這個套機制可以實現(xiàn)CPU頻率等資源的管理。
關(guān)于高通這套Framework的具體架構(gòu),可以參考最后附錄中的參考資料的相關(guān)文章,我們只需要知道:
在Java層 /android/util/BoostFramework.java類封裝了一些基本的API提供給framework層調(diào)用。
1.2 實現(xiàn)
通過閱讀BoostFramework的源碼,可以發(fā)現(xiàn)其實現(xiàn)主要是對 QPerformance.jar 和UxPerformance.ja r中的 API 進行了一層反射調(diào)用包裝。那么一樣的,我們也可以通過封裝對 BoostFrameWork 類的調(diào)用提供提頻能力。
不過這些函數(shù)似乎并不是默認公開的內(nèi)容,直接通過google搜索 并沒有找到關(guān)于BoostFramwork或者高通Performance API的相關(guān)信息。最后還是通過其他各種關(guān)鍵字檢索,終于找到了部分有效信息。
圖片
圖片
通過對應(yīng)API文檔及使用示例得知perfLocakAcquire 該函數(shù)接受 2個參數(shù),第一個參數(shù)為持續(xù)時間、第二個參數(shù)為一個int數(shù)組,表示具體的操作,數(shù)組中的內(nèi)容為 k-v 結(jié)構(gòu)形式,比如 [config1,value,config2,value] . 該函數(shù)執(zhí)行時會返回一個 PerfLock句柄,后續(xù)通過調(diào)用 perfLockReleaseHandler 可以提前取消之前的操作。
這里簡單羅列一些配置項對應(yīng)的值:
/**
* 是否允許CPU進入深度低功耗模式, 對應(yīng) /dev/cpu_dma_latency, 默認空,不允許則設(shè)置為1
*/
const val MPCTLV3_ALL_CPUS_PWR_CLPS_DIS = 0x40400000
/**
* 對應(yīng)控制小核最小頻率
*/
const val MPCTLV3_MIN_FREQ_CLUSTER_LITTLE_CORE_0 = 0x40800100
/**
* 對應(yīng)控制小核最大頻率
*/
const val MPCTLV3_MAX_FREQ_CLUSTER_LITTLE_CORE_0 = 0x40804100
/**
* 對應(yīng)控制大核最小頻率
*/
const val MPCTLV3_MIN_FREQ_CLUSTER_BIG_CORE_0 = 0x40800000
/**
* 對應(yīng)控制大核最大頻率
*/
const val MPCTLV3_MAX_FREQ_CLUSTER_BIG_CORE_0 = 0x40804000
/**
* 對應(yīng)控制超大核最小頻率
*/
const val MPCTLV3_MIN_FREQ_CLUSTER_PLUS_CORE_0 = 0x40800200;
/**
* 對應(yīng)控制超大核最小頻率
*/
const val MPCTLV3_MAX_FREQ_CLUSTER_PLUS_CORE_0 = 0x40804200
/**
* 不太清楚,似乎是調(diào)度加速
*/
const val MPCTLV3_SCHED_BOOST = 0x40C00000;
完整的配置項定義,可以參考:https://github.com/Knight-ZXW/AppOptimizeFramework/blob/master/docs/qualcomms.txt。
另外,如何確定我們的設(shè)備包含高通的這套性能調(diào)控SDK呢?可以通過查看你的Android設(shè)備存儲路徑/system/framework/路徑,如果包含了 QPerformance.jar 及 QXPerformance.jar 就表示接入了SDK。
圖片
根據(jù)上面的知識點,最終該工具類完整的實現(xiàn)代碼如下:
首先在init 函數(shù)中反射并獲取 "android.util.BoostFramework”類的相應(yīng)函數(shù)
提供 boostCpu 函數(shù),該函數(shù)傳入一個參數(shù),表示提升CPU頻率持續(xù)多久,該函數(shù)內(nèi)部調(diào)用perfLockAcquire 函數(shù) 將所有CPU頻率提升到最高值
提供 stopBoost 函數(shù),該函數(shù)會將前面調(diào)用的boostCpu 效果提前取消。
package com.knightboost.optimize.cpuboost
import android.content.Context
import java.lang.reflect.Method
import java.util.concurrent.CopyOnWriteArrayList
class QcmCpuPerformance : CpuPerformance {
companion object {
const val TAG = "QcmCpuPerformance";
/**
* 是否允許CPU進入深度低功耗模式, 對應(yīng) /dev/cpu_dma_latency, 默認空,不允許則設(shè)置為1
*/
const val MPCTLV3_ALL_CPUS_PWR_CLPS_DIS = 0x40400000
/**
* 設(shè)置小核最小頻率,十六進制
*/
const val MPCTLV3_MIN_FREQ_CLUSTER_LITTLE_CORE_0 = 0x40800100
/**
* 設(shè)置小核最大頻率, 十六進制
*/
const val MPCTLV3_MAX_FREQ_CLUSTER_LITTLE_CORE_0 = 0x40804100
/**
* 設(shè)置大核最小頻率,十六進制
*/
const val MPCTLV3_MIN_FREQ_CLUSTER_BIG_CORE_0 = 0x40800000
/**
* 設(shè)置大核最大頻率,十六進制
*/
const val MPCTLV3_MAX_FREQ_CLUSTER_BIG_CORE_0 = 0x40804000
const val MPCTLV3_MIN_FREQ_CLUSTER_PLUS_CORE_0 = 0x40800200;
const val MPCTLV3_MAX_FREQ_CLUSTER_PLUS_CORE_0 = 0x40804200
/**
* 調(diào)度優(yōu)化? 啟動 值為01
*/
const val MPCTLV3_SCHED_BOOST = 0x40C00000;
}
var initSuccess = false
lateinit var acquireFunc: Method
lateinit var mPerfHintFunc: Method
lateinit var releaseFunc: Method
lateinit var frameworkInstance: Any
var boostHandlers = CopyOnWriteArrayList<Int>()
/**
* 配置: 請求將所有CPU核心頻率拉滿,并禁止進入深入低功耗模式
*/
private var CONFIGS_FREQUENCY_HIGH = intArrayOf(
MPCTLV3_SCHED_BOOST, 1,
MPCTLV3_ALL_CPUS_PWR_CLPS_DIS, 1,
MPCTLV3_MAX_FREQ_CLUSTER_BIG_CORE_0, 0xFFF,
MPCTLV3_MAX_FREQ_CLUSTER_LITTLE_CORE_0, 0xFFF,
MPCTLV3_MIN_FREQ_CLUSTER_BIG_CORE_0, 0xFFF,
MPCTLV3_MIN_FREQ_CLUSTER_LITTLE_CORE_0, 0xFFF,
MPCTLV3_MIN_FREQ_CLUSTER_PLUS_CORE_0, 0xFFF,
MPCTLV3_MAX_FREQ_CLUSTER_PLUS_CORE_0, 0xFFF,
)
var DISABLE_POWER_COLLAPSE = intArrayOf(MPCTLV3_ALL_CPUS_PWR_CLPS_DIS, 1)
/**
* 初始化CpuBoost 核心功能
*/
override fun init(context: Context): Boolean {
try {
val boostFrameworkClass = Class.forName("android.util.BoostFramework")
val constructor = boostFrameworkClass.getConstructor(Context::class.java)
?: return false
frameworkInstance = constructor.newInstance(context)
acquireFunc = boostFrameworkClass.getDeclaredMethod(
"perfLockAcquire", Integer.TYPE, IntArray::class.java
)
mPerfHintFunc = boostFrameworkClass.getMethod(
"perfHint", Int::class.javaPrimitiveType, String::class.java, Int::class.javaPrimitiveType, Int::class.javaPrimitiveType
)
releaseFunc = boostFrameworkClass.getDeclaredMethod(
"perfLockReleaseHandler", Integer.TYPE
)
initSuccess = true
return true
} catch (e: Exception) {
initSuccess = false
CpuBoostManager.boostErrorLog(TAG, "init failed", e)
return false
}
}
/**
* 提升所有核心CPU頻率到最高頻率
*/
override fun boostCpu(duration: Int): Boolean {
if (!initSuccess) return false
return try {
perfLockAcquire(duration, DISABLE_POWER_COLLAPSE)
perfLockAcquire(duration, CONFIGS_FREQUENCY_HIGH)
return true
} catch (e: Exception) {
CpuBoostManager.boostErrorLog(TAG, "boostCpuFailed", e)
false
}
}
/**
* Toggle off all optimizations requested Immediately.
* Use this function if you want to release before the time duration ends.
*
* 這個函數(shù)并不強制調(diào)用,只用于提前取消所有已配置的加速效果。
*/
override fun stopBoost() {
val handlers = boostHandlers.toTypedArray()
for (handler in handlers) {
try {
releaseFunc.invoke(frameworkInstance, handler)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
/**
* Toggle on all optimizations requested.
* @param duration: The maximum amount of time required to hold the lock.
* Only a positive integer value in milliseconds will be accepted.
* You may explicitly call perfLockRelease before the timer expires.
* @param list Enter all optimizations required. Only the optimizations in the
* table below are supported. You can only choose one optimization
* from each of the numbered sections in the table. Incorrect or
* unsupported optimizations will be ignored.
*
* NOTE: Enter the optimizations required in the order they appear in the table.
*/
private fun perfLockAcquire(duration: Int, list: IntArray): Int {
val handler = acquireFunc.invoke(frameworkInstance, duration, list) as Int;
if (handler > 0) {
boostHandlers.add(handler)
}
return handler
}
}
1.3 驗證
通過讀取:
/sys/devices/system/cpu/cpu$cpuIndex/cpufreq/下的文件可以獲取對應(yīng)CPU所能運行的最小、最大、以及當前的頻率。
在提頻前,當前設(shè)備的CPU頻率信息如下:
可以發(fā)現(xiàn)提頻前,0~3 這些小核中,3個運行在最大調(diào)頻頻率,1個運行在最小調(diào)頻頻率。4~6中核都運行在最小頻率,7號大核直接摸魚運行在最小頻率。
在提頻后,運行數(shù)據(jù)如下:
可以看出,進行提頻后,所有核心都運行在最大頻率上,整機頻率相比之前提升30%, 當然在實際運行過程中,提頻前的工作頻率并不會這么低,這里的數(shù)據(jù)是從CPU幾乎空閑狀態(tài)到直接滿頻的情況。
2、線性CPU親和性
2.1 概念
根據(jù)wikipedia上的解釋,通過設(shè)置CPU親和性可以控制線程在哪些CPU上運行。
通過CPU親和性的概念可以提高線程的運行效率,比如由于CPU存在緩存機制,通過CPU親和性(CPU Affinity)讓同一個線程被重新調(diào)度時,盡量調(diào)度到同一個處理器上,這樣就可以可以避免不必要的 Cache Miss。另一種情況,比如對于一組相同的任務(wù),它們需要訪問的內(nèi)存大部分是相同的,如果控制這組任務(wù)調(diào)度在相同的CPU上,也可以共享相同的cache,從而提高程序的訪問效率。
CPU親和性分為2種,分別為軟親和性和硬親和性:
- 軟親和性: linux系統(tǒng)會盡可能將一個進程保持在指定的CPU上運行,但不嚴格保證,當所指定的CPU非常繁忙時,它可以遷移到其他空閑CPU上執(zhí)行
- 硬親和性:linux系統(tǒng)允許指定某個進程運行在特定的一個或一組CPU上,并且只能運行在這些特定的CPU上。
在下文中,我們討論的親和性控制將只涉及到硬親和性。
2.2 親和性控制
2.2.1 API
在linux系統(tǒng)中,可以通過taskset命令或者程序中調(diào)用 sched_setaffinity 指定線程的CPU親和性。
taskset的具體用法為 taskset [-ap] [mask] [PID]
這里的mask指的是CPU掩碼,CPU掩碼描述了具體哪些CPU,以8核CPU為例,
二進制 00000011 (十進制值為3), 表示CPU序號1 和2, 當調(diào)用命令 tasket -p 3 2001 表示序號為2001的進程將只會運行在 cpu 1 或2 上。也就是說CPU掩碼根據(jù)對應(yīng)二進制位置及其0或1的值,表示某個線程的CPU相關(guān)親和性。
當我嘗試在 Android設(shè)備上直接調(diào)用 taskset命令,系統(tǒng)提示無權(quán)限。
為了進一步了解 taskset程序的實現(xiàn),為后續(xù)我們自己實現(xiàn)CPU控制提供參考,這里研究了一下其實現(xiàn)代碼。該工具的實現(xiàn)源碼在 util-linux項目中。
上面提示的 `failed to get xx's affinity`其實是在調(diào)用 sched_getaffinity 函數(shù)時就失敗了。這里我的設(shè)備未Root,因此猜測原因為 sched_setaffinity 、sched_getaffinity 底層涉及的系統(tǒng)調(diào)用只有當前進程才有權(quán)限控制其自身的affinity屬性。
通過其源碼實現(xiàn)可以發(fā)現(xiàn)該工具實現(xiàn)就是套了層皮,底層實現(xiàn)還是調(diào)用的 sched_setaffinity函數(shù)。
2.3 應(yīng)用層控制實現(xiàn)
有了上述背景,在native層編寫一個CPU親和性控制的函數(shù)就比較簡單了,主要涉及到sched.h頭文件的幾個函數(shù), 以下為最終實現(xiàn)示例代碼
#include <jni.h>
#include "unistd.h"
#include "sched.h"
#include "android/log.h"
Java_com_knightboost_optimize_cpuboost_ThreadCpuAffinityManager_setCpuAffinity(JNIEnv *env,
jclass clazz,
jint tid,
jintArray cpu_set) {
if (tid <= 0) {
tid = gettid();
}
// 獲取當前CPU核心數(shù)
int cpu_count = sysconf(_SC_NPROCESSORS_CONF);
jsize size = env->GetArrayLength(cpu_set);
jint bind_cpus[size];
env->GetIntArrayRegion(cpu_set, 0, size, bind_cpus);
cpu_set_t mask;
CPU_ZERO(&mask);
for (jint cpu : bind_cpus) {
if (cpu > 0 && cpu < cpu_count) {
CPU_SET(cpu, &mask); //設(shè)置對應(yīng)cpu位置的值為1
} else {
__android_log_print(ANDROID_LOG_ERROR,
"TCpuAffinity",
"try bind illegal cpu index %d",cpu);
}
}
int code = sched_setaffinity(tid, sizeof(mask), &mask);
if (code == 0) {
// return success
return JNI_TRUE;
} else {
__android_log_print(ANDROID_LOG_ERROR,
"TCpuAffinity",
"setCpuAffinity() failed code %d",code);
// return failed
return JNI_FALSE;
}
}
該函數(shù)中,首先獲取了當前的CPU核心數(shù),接下來創(chuàng)建一個 cpu_set_t mask變量,調(diào)用宏函數(shù) CPU_SET 將對應(yīng)位置的二進制值設(shè)置為1, 最后調(diào)用 sched_setaffinity 設(shè)置相應(yīng)線程的CPU親和性。
在實際應(yīng)用場景中,我們可以將某個線程需要執(zhí)行繁重任務(wù)時,將它綁定到大核上,當任務(wù)執(zhí)行結(jié)束時,再還原原始的CPU親和性值或者將其CPU親和性值重置為所有CPU。
2.4 驗證
到目前所講的都還是理論階段,那么我們?nèi)绾未_認修改線程的CPU親和性之后,這個線程確實被遷移到目標CPU上執(zhí)行了呢?
在之前寫過的一篇CPU相關(guān)的文章《Android 高版本采集系統(tǒng)CPU使用率的方式》中,我們提及了 stat文件記錄了線程當前指向狀態(tài)的相關(guān)信息。根據(jù)linux手冊, 第 39 處的值就表示了該線程最后運行的CPU。
因此通過讀取該文件,我們就可以獲取線程所運行在哪個CPU上:
/**
* 獲取目標線程最后運行在哪個CPU
*/
fun getLastRunOnCpu(tid:Int):Int{
var path = "/proc/${android.os.Process.myPid()}/task/${tid}/stat"
try {
val content = File(path).readText()
var arrays = StringUtil.splitWorker(content,' ')
var cpu = arrays[38]
return cpu.toInt()
}catch (e:Exception){
// this task may already have ended
return -1;
}
}
這里我們需要獲取Java線程對應(yīng)操作系統(tǒng)的線程id(tid),關(guān)于 tid 的獲取可以參考之前的文章:《Android虛擬機線程啟動過程解析, 獲取Java線程真實線程Id的方式》。
我們通過獲取Java Thread對象的 nativePeer值,這個地址對應(yīng)了Android native層的Thread對象指針地址,再根據(jù)tls_32bit_sized_values結(jié)構(gòu)的tid屬性偏移值,進行類型強轉(zhuǎn),從而獲取系統(tǒng)線程id。
在demo中,在修改目標線程CPU后,我們可以持續(xù)打印這個值,以驗證綁核是否成功。
這里我嘗試將目標線程的 affinity修改為大核(CPU序號7),打印結(jié)果如下:
可以看到,在執(zhí)行修改前,目標線程的CPU親和性為0~7核心,且最近1秒基本運行在CPU核心2上,在修改CPU親和性為CPU7后, 目標線程只會運行在CPU7 上。這驗證了功能確實生效了。
3、線程優(yōu)先級
3.1 概念
除了CPU頻率、線程CPU親和性,線程的優(yōu)先級也會影響線程對CPU的使用,線程優(yōu)先級更高意味該線程有更高的概率獲得CPU的執(zhí)行,分配到更多的CPU時間片。
3.2 實現(xiàn)
在Android平臺下,可以通過Process.setThreadPriority(int tid, int priority) ,這適用于無法獲取目標線程的Thread對象,只知道目標線程tid的情況。
當然,如果能夠獲取到Thread對象,也可以通過 Thred對象的 setPriority(int newPriority)設(shè)置。
需要注意的是,這2個函數(shù)優(yōu)先級int值的定義和范圍是不同的,第一個函數(shù)是Android系統(tǒng)提供的Java接口,它的優(yōu)先級沿用linux對線程的優(yōu)先級定義 (-20~19),而第二個函數(shù)是Java jdk提供的,它的優(yōu)先級范圍為1~10。
另外,Process.setThreadPriority(int tid, int priority) 這里的tid 需要的是實際的操作系統(tǒng)線程ID,而不是Java中Thread的id。
另一方面,Thread.setPriority(int newPriority) 函數(shù)設(shè)置的優(yōu)先級并沒有達到最大值,我們測試下使用Thread對象的設(shè)置優(yōu)先級函數(shù)為最高值(Thread.MAX_PRIORITY) 之后的nice值 ,并和 Process.setThreadPriority進行比較,測試代碼如下:
Thread{
var currentThread = Thread.currentThread()
var tid = ArtThread.getTid(currentThread)
Log.e("priorityTest","當前線程 $tid" +
" java優(yōu)先級 ${currentThread.priority} nice值 ${ThreadUtil.getNice(tid)}")
currentThread.priority=Thread.MAX_PRIORITY;
Log.e("priorityTest","使用 Thread.setPriority 設(shè)置最高優(yōu)級10 后 nice值 ${ThreadUtil.getNice(tid)}")
Process.setThreadPriority(tid,-20)
Log.e("priorityTest","使用 Process.setThreadPriority 設(shè)置最高優(yōu)級-20 后 nice值 ${ThreadUtil.getNice(tid)}")
}.start()
測試結(jié)果如下:
由此可見,如果希望最大程度提高線程優(yōu)先級的話,還是需要使用Process的函數(shù)。
那么這里為什么Android系統(tǒng)下通過Thread.setPriority 設(shè)置的最高優(yōu)先級nice值為什么為-8呢?通過跟蹤native層代碼路徑發(fā)現(xiàn),這里Java線程優(yōu)先級的1~10 在底層的取值 其實是映射了使一個數(shù)組,該數(shù)組存儲了對應(yīng)設(shè)置的nice優(yōu)先級,其中的最高優(yōu)先級10對應(yīng)的 ANDROID_PRIORITY_URGENT_DISPLAY 對應(yīng)的nice值就為-8
Android系統(tǒng)對于什么情況下使用什么nice值 完整定義如下:
3.3 驗證
為了驗證設(shè)置線程優(yōu)先級對線程獲得CPU時間片的提升效果,我們創(chuàng)建一組工作線程,并同時執(zhí)行,每個線程會執(zhí)行一個類似死循環(huán)的工作,這樣每個線程都不會主動讓出CPU,工作5秒后,計算當前線程得到CPU執(zhí)行的時間。為了更好對比線程優(yōu)先級對CPU時間片分配的影響,我們將這組線程統(tǒng)一綁定到一個核心上,這樣可以更好的觀測線程優(yōu)先級對CPU時間片分配的的影響。
根據(jù)輸出結(jié)果可以發(fā)現(xiàn),優(yōu)先級為-20的線程占用了cpu98%的執(zhí)行時間,其他線程幾乎沒得到執(zhí)行。
而如果將線程優(yōu)先級修改為0,也就是默認的線程優(yōu)先級,那么這4個線程將會得到幾乎相同的執(zhí)行時間。
圖片
從這個結(jié)果看,線程優(yōu)先級的效果還是比較明顯的。
不過在實際情況中,如果這些線程并沒有特別指定在某個CPU執(zhí)行,那么它們可能會在任何CPU上執(zhí)行,系統(tǒng)會自動將線程調(diào)度到其他不繁忙的CPU上。
以下是指定了 task4的優(yōu)先級,但并沒有綁定CPU核的情況輸出的結(jié)果:
圖片
這里有2個信息:
- 一開始task可能會被分配在相同的CPU上,但由于我們的任務(wù)幾乎是一個空循環(huán)任務(wù),對CPU的使用率較高,此時每個任務(wù)都無法得到足夠的CPU時間片執(zhí)行,而1、4核心可能又幾乎是空閑的,因此當系統(tǒng)發(fā)現(xiàn)某些CPU負責較高時,系統(tǒng)自動將部分線程任務(wù)遷移到空閑的CPU上執(zhí)行
- 由于線程被分配的不同的CPU上,因此這幾個線程之間不存在優(yōu)先級比較關(guān)系,因此每個任務(wù)都得到了充足的CPU時間執(zhí)行。
從這里我們也可以看出,不合理的強綁定CPU核心, 有時候可能會起到相反的效果。
4、總結(jié)
本文分享了Android系統(tǒng)下自主控制CPU頻率、線程指定核心和優(yōu)先級的方式,不過這些能力需要具體落實到業(yè)務(wù)場景才能夠獲得實際的收益。后續(xù)我們將在進程冷啟動、Activity啟動階段、頁面滑動、幀渲染線程優(yōu)化、等業(yè)務(wù)場景進行嘗試。
參考資料:
- 升頻計算公式:https://docs.kernel.org/scheduler/schedutil.html
- 高通BoostFramework概要介紹: https://juejin.cn/post/7141196697555714079
- BosstFrameowrk:https://gerrit.pixelexperience.org/plugins/gitiles/frameworks_base/+/0420df35ae49ed11d503571aa76f80a154f1b4ac/core/java/android/util/BoostFramework.java
- Android虛擬機線程啟動過程解析, 獲取Java線程真實線程id的方式:https://juejin.cn/post/7138690370694545415
- Android 高版本采集系統(tǒng)CPU使用率的方式:https://juejin.cn/post/7135034198158475300
- https://lwn.net/Articles/792502/
- https://dumps.tadiphone.dev/dumps/oneplus/op516el1/-/blob/qssi-user-13-SKQ1.220519.001-S.202208250304-release-keys--ALLNET/vendor/etc/powerhint.xml
- https://deepinout.com/qcom-camx-debug-user-guide/camx-perf-debug-user-guide/qcom-perflock-usage.html
- http://www.manongzj.com/blog/31-sgltmktizd.html
- powerhint.xml、powerhint.xml
- https://en.wikipedia.org/wiki/Processor_affinity