一,通过设置绘画的RenderThread线程的调度策略来提高性能
默认情况下每个进程的绘画线程RenderThread的调度策略是SCHED_OTHER, 并且优先级为-10.
设置系统属性sys.use_fifo_ui为1后,则表示每个进程的绘画线程RenderThread的调度策略变更为SCHED_FIFO,并且实时优先级为1.
调度器 | 名称 | 解释 |
SCHED_OTHER | 默认 | 标准round-robin分时共享策略 |
SCHED_BATCH | 批处理调度 | 针对具有batch风格(批处理)进程的调度策略 |
SCHED_IDLE | 空闲调度 | 针对优先级非常低的适合在后台运行的进程 |
SCHED_FIFO | 先进先出 | 实时调度策略 |
SCHED_RR | 循环调度 | 实时调度策略 |
二,DEBUG
PS 参数
#ps -eo class,cmd
Command line field types:
ARGS CMDLINE minus initial path CMD Thread name (/proc/TID/stat:2)
CMDLINE Command line (argv[]) COMM EXE filename (/proc/PID/exe)
COMMAND EXE path (/proc/PID/exe) NAME Process name (PID's argv[0])
Process attribute field types:
S Process state:
R (running) S (sleeping) D (device I/O) T (stopped) t (trace stop)
X (dead) Z (zombie) P (parked) I (idle)
Also between Linux 2.6.33 and 3.13:
x (dead) K (wakekill) W (waking)
SCH Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)
STAT Process state (S) plus:
< high priority N low priority L locked memory
s session leader + foreground l multithreaded
%CPU Percentage of CPU time used %MEM RSS as % of physical memory
%VSZ VSZ as % of physical memory ADDR Instruction pointer
BIT 32 or 64 C Total %CPU used since start
CPU Which processor running on DIO Disk I/O
DREAD Data read from disk DWRITE Data written to disk
ELAPSED Elapsed time since PID start F Flags 1=FORKNOEXEC 4=SUPERPRIV
GID Group ID GROUP Group name
IO Data I/O LABEL Security label
MAJFL Major page faults MINFL Minor page faults
NI Niceness (static 19 to -20) PCY Android scheduling policy
PGID Process Group ID PID Process ID
PPID Parent Process ID PR Prio Reversed (dyn 39-0, RT)
PRI Priority (dynamic 0 to 139) PSR Processor last executed on
READ Data read RES Short RSS
RGID Real (before sgid) Group ID RGROUP Real (before sgid) group name
RSS Resident Set Size (DRAM pages) RTPRIO Realtime priority
RUID Real (before suid) user ID RUSER Real (before suid) user name
SHR Shared memory STIME Start time (ISO 8601)
SWAP Swap I/O SZ 4k pages to swap out
TCNT Thread count TID Thread ID
TIME CPU time consumed TIME+ CPU time (high precision)
TTY Controlling terminal UID User id
USER User name VIRT Virtual memory size
VSZ Virtual memory size (1k units) WCHAN Wait location in kernel
WRITE Data written
https://cloud.tencent.com/developer/article/1370262
- static_prio静态优先级: 不会时间而改变,内核也不会修改,只能通过系统调用改变nice值的方法区修改。优先级映射公式: static_prio = MAX_RT_PRIO + nice + 20,其中MAX_RT_PRIO = 100,那么取值区间为[100, 139];对应普通进程;
- rt_priority实时优先级:只对实时进程有意义,取值区间为[0, MAX_RT_PRIO -1],其中MAX_RT_PRIO = 100,那么取值区间为[0, 99];对应实时进程;
- prio动态优先级: 调度程序通过增加或减少进程静态优先级的值,来达到奖励IO消耗型或惩罚cpu消耗型的进程,调整后的进程称为动态优先级。区间范围[0, MX_PRIO-1],其中MX_PRIO = 140,那么取值区间为[0,139];
- normal_prio普通优先级 表示基于进程的静态优先级static_prio和调度策略计算出的优先级. 因此即使普通进程和实时进程具有相同的静态优先级, 其普通优先级也是不同的, 进程分叉(fork)时, 子进程会继承父进程的普通优先级
1. 设置RenderThread线程的调度策略为SCHED_FIFO
#setprop sys.use_fifo_ui 1
# ps -AT -l | grep -E "26946|PID" | grep -E "RenderThread|PID"
F S UID PID TID PPID C PRI NI BIT SZ WCHAN TTY TIME CMD
1 S 10113 26946 26974 25202 0 41 0 32 335037 SyS_epoll_wait ? 00:00:00 RenderThread
# ps -AT -o TID,SCH,NI,PRI,PR,RTPRIO,CMD | grep -E "26974 |TID" | grep -E "RenderThread|TID"
TID SCH NI PRI PR RTPRIO CMD
26974 1 0 41 -2 1 RenderThread
# cat proc/26974/sched
policy : 1
prio : 98 (MAX_RT_PRIO - 1 - RTPRIO = 99 - 1 = 98 )
2. 设置RenderThread线程的调度策略为SCHED_OTHER
#setprop sys.use_fifo_ui 0
# ps -AT -l | grep -E "22599|PID" | grep -E "RenderThread|PID"
F S UID PID TID PPID C PRI NI BIT SZ WCHAN TTY TIME CMD
1 S 10113 22599 22626 20804 0 29 -10 32 335037 SyS_epoll_wait ? 00:00:00 RenderThread
# ps -AT -o TID,SCH,NI,PRI,PR,RTPRIO,CMD | grep -E "22626|TID" | grep -E "RenderThread|TID"
TID SCH NI PRI PR RTPRIO CMD
22626 0 -10 29 10 - RenderThread
# cat proc/22626/sched
policy : 0
prio : 110 (MAX_RT_PRIO + (20 + NI) = 100 + 10 = 110)
另外线程最终实际的优先级可以如此计算: 100 + PR
3. 重置线程优先级, 该版本renice只能设置nice值【-20, 19】
# ps -AT -o TID,SCH,NI,PRI,PR,RTPRIO,CMD | grep -E "8768|TID"
TID SCH NI PRI PR RTPRIO CMD
8768 0 -4 23 16 - RenderThread
# renice -n 10 8768 //把线程8768的原有nice值加上10
# ps -AT -o TID,SCH,NI,PRI,PR,RTPRIO,CMD | grep -E "8768|TID"
TID SCH NI PRI PR RTPRIO CMD
8768 0 6 13 26 - RenderThread
三,代码
1. @ActivityManagerService.java (frameworks\base\services\core\java\com\android\server\am)
// Whether we should use SCHED_FIFO for UI and RenderThreads.
boolean mUseFifoUiScheduling = false;
public ActivityManagerService(Context systemContext, ActivityTaskManagerService atm) {
if (SystemProperties.getInt("sys.use_fifo_ui", 0) != 0 ||
SystemProperties.get("ro.vendor.mtk_perf_plus").equals("1")) {
mUseFifoUiScheduling = true;
}
...
}
public static boolean scheduleAsFifoPriority(int tid, boolean suppressLogs) {
try {
Process.setThreadScheduler(tid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
return true;
} catch (IllegalArgumentException e) {
if (!suppressLogs) {
Slog.w(TAG, "Failed to set scheduling policy, thread does not exist:\n" + e);
}
} catch (SecurityException e) {
if (!suppressLogs) {
Slog.w(TAG, "Failed to set scheduling policy, not allowed:\n" + e);
}
}
return false;
}
@Override
public void setRenderThread(int tid) {
synchronized (this) {
ProcessRecord proc;
int pid = Binder.getCallingPid();
//如果是自身system_server进程,则一般是截屏或录屏后台操作,所以不需要高优先级
if (pid == Process.myPid()) {
demoteSystemServerRenderThread(tid);
return;
}
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(pid);
if (proc != null && proc.renderThreadTid == 0 && tid > 0) {
// ensure the tid belongs to the process
if (!isThreadInProcess(pid, tid)) {
throw new IllegalArgumentException(
"Render thread does not belong to process");
}
proc.renderThreadTid = tid;
if (DEBUG_OOM_ADJ) {
Slog.d("UI_FIFO", "Set RenderThread tid " + tid + " for pid " + pid);
}
// promote to FIFO now, 必须是最前台进程的RenderThread,才提升调度策略
if (proc.getCurrentSchedulingGroup() == ProcessList.SCHED_GROUP_TOP_APP) {
if (DEBUG_OOM_ADJ) Slog.d("UI_FIFO", "Promoting " + tid + "out of band");
if (mUseFifoUiScheduling) { //改变调度策略为实时FIFO,并且优先级为1
//SCHED_RESET_ON_FORK表示策略只作用于主动设置的进程,不会传递到子进程
setThreadScheduler(proc.renderThreadTid,
SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
} else {//如果不改变默认的调度策略,则设置优先级为-10
setThreadPriority(proc.renderThreadTid, TOP_APP_PRIORITY_BOOST);
}
}
} else {
if (DEBUG_OOM_ADJ) {
Slog.d("UI_FIFO", "Didn't set thread from setRenderThread? " +
"PID: " + pid + ", TID: " + tid + " FIFO: " +
mUseFifoUiScheduling);
}
}
}
}
}
//如果是自身system_server进程,则一般是截屏或录屏后台操作,所以不需要高优先级
private void demoteSystemServerRenderThread(int tid) {
setThreadPriority(tid, Process.THREAD_PRIORITY_BACKGROUND);
}
2. @OomAdjuster.java (frameworks\base\services\core\java\com\android\server\am)
private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
final int curSchedGroup = app.getCurrentSchedulingGroup();
if (app.setSchedGroup != curSchedGroup) {
int oldSchedGroup = app.setSchedGroup;
if (app.waitingToKill != null && app.curReceivers.isEmpty()
} else {
int processGroup;
switch (curSchedGroup) {
case ProcessList.SCHED_GROUP_BACKGROUND:
processGroup = THREAD_GROUP_BG_NONINTERACTIVE;
break;
case ProcessList.SCHED_GROUP_TOP_APP:
case ProcessList.SCHED_GROUP_TOP_APP_BOUND:
processGroup = THREAD_GROUP_TOP_APP;
break;
case ProcessList.SCHED_GROUP_RESTRICTED:
processGroup = THREAD_GROUP_RESTRICTED;
break;
default:
processGroup = THREAD_GROUP_DEFAULT;
break;
}
try {
//如果进程切换到最前面:mUseFifoUiScheduling=true则把该进程的UI线程和RenderThread线程都提升为FIFO调度策略,优先级为1; mUseFifoUiScheduling=false则该进程的UI线程和RenderThread线程的优先级提升为TOP_APP_PRIORITY_BOOST=-10
if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
// do nothing if we already switched to RT
if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
if (mService.mUseFifoUiScheduling) {
// Switch UI pipeline for app to SCHED_FIFO
app.savedPriority = Process.getThreadPriority(app.pid);
mService.scheduleAsFifoPriority(app.pid, /* suppressLogs */true);
if (app.renderThreadTid != 0) {
mService.scheduleAsFifoPriority(app.renderThreadTid,
/* suppressLogs */true);
} else {
if (DEBUG_OOM_ADJ) {
Slog.d("UI_FIFO", "Not setting RenderThread TID");
}
}
} else {
// Boost priority for top app UI and render threads
setThreadPriority(app.pid, TOP_APP_PRIORITY_BOOST);
if (app.renderThreadTid != 0) {
try {
setThreadPriority(app.renderThreadTid,
TOP_APP_PRIORITY_BOOST);
} catch (IllegalArgumentException e) {
// thread died, ignore
}
}
}
}
} else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
//如果进程切换到后台
if (mService.mUseFifoUiScheduling) {
try {
// 还原UI线程调度为SCHED_OTHER, 优先级为原来默认值0; 还原RenderThread线程调度为SCHED_OTHER,优先级为-4(为啥选择-4?)
setThreadScheduler(app.pid, SCHED_OTHER, 0);
setThreadPriority(app.pid, app.savedPriority);
if (app.renderThreadTid != 0) {
setThreadScheduler(app.renderThreadTid,
SCHED_OTHER, 0);
setThreadPriority(app.renderThreadTid, -4);
}
} catch (IllegalArgumentException e) {
Slog.w(TAG,
"Failed to set scheduling policy, thread does not exist:\n"
+ e);
} catch (SecurityException e) {
Slog.w(TAG, "Failed to set scheduling policy, not allowed:\n" + e);
}
} else {
// Reset priority for top app UI and render threads
setThreadPriority(app.pid, 0);
if (app.renderThreadTid != 0) {
setThreadPriority(app.renderThreadTid, 0);
}
}
}
}
3. @ThreadedRenderer.java (frameworks\base\core\java\android\view)
public final class ThreadedRenderer extends HardwareRenderer {
}
4. @HardwareRenderer.java (frameworks\base\graphics\java\android\graphics)
synchronized void init(long renderProxy) {
if (mInitialized) return;
mInitialized = true;
initSched(renderProxy);
initGraphicsStats();
}
private void initSched(long renderProxy) {
try {
int tid = nGetRenderThreadTid(renderProxy); //得到RenderThread线程TID
ActivityManager.getService().setRenderThread(tid); //设置该线程调度策略和优先级
} catch (Throwable t) {
Log.w(LOG_TAG, "Failed to set scheduler for RenderThread", t);
}
}
四,如何设置
@device/mediatek/vendor/common/device.mk
1. 添加sys.use_fifo_ui=1定义
PRODUCT_PROPERTY_OVERRIDES += sys.use_fifo_ui=1
# For performance tuning ifeq ($(strip $(MTK_PERF_PLUS)),yes) PRODUCT_PROPERTY_OVERRIDES += ro.vendor.mtk_perf_plus=1 endif
2. 定义MTK_PERF_PLUS
@device/fih/BT1/ProjectConfig.mk
MTK_PERF_PLUS = no ---> MTK_PERF_PLUS = yes
五,思考
1. 设置线程的调度策略为FIFO时,是否优先级过低(设为1)?当调度策略为FIFO时,设置的实时优先级数越小则表示优先级真是小。实时优先级的范围[0,99]
setThreadScheduler(proc.renderThreadTid, SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
比如把实时优先级提高到15或50?
2. 同理当调度策略为OTHER时,是否线程优先级过低(-10)。TOP_APP_PRIORITY_BOOST = -10, setThreadPriority(proc.renderThreadTid, TOP_APP_PRIORITY_BOOST);
比如把线程优先级提高到-20或-19?
3. 当APP不在最前面(Top APP)时,UI线程和RenderThread线程都要降低优先级到0或-4, 能不能降到更低呢?比如10