目录
背景
一、性能优化关键点众览
二、性能关键技术
1. CPU调度
1.1 调度单位
1.2 线程状态
1.3 线程优先级
1.4 CPU调度策略
1.4.1 调度器
1.4.2 CPUFreq Governor
1.4.3 CGroup
1.5 PerfLock
1.6 进程冻结
1.7 LMKD
1.7.1 水线
1.7.2 ADJ
1.8 温控
1.8.1 温控配置和策略
2. IO调度
3. 内存分配
3.1 内存分配器
3.2 内存压缩和回收
3.3 Camera内存
4. 锁机制
5. IPC
6. Graphics Framework
7. HW Performance
三、性能分析工具
1. Perfetto/Systrace/Atrace
2. Simpleperf & 火焰图
3. Android Profiler
4. ftrace
5. Linux perf工具
6. eBPF
四、Camera场景性能分析
1.启动
1.1 冷启动
1.1.1 关键路径
1.1.2 流程分析
1.1.3 关键点
1.2 热启动
1.2 预览卡顿
2. 模式切换
3. Lens切换
4. 拍照
5. 录像
五、优化点
1. 异步化
2. 提前和延后
3. IO
4. 调度优化
5. 业务逻辑优化
小结
背景
做camera性能优化方面的工作有几年了,想写一篇文章简单介绍一下其中涉及到的具体技术,这篇文章会大概介绍一下涉及到的技术。
一、性能优化关键点众览
性能优化分析方向:给了多少资源和做了多少事。
- 给多少资源主要是硬件方面资源,比如CPU频率、GPU频率、NPU频率、外设IO总线的clock、memory大小等,需要重点关注分配给当前task的资源,尤其是CPU时间片。在平衡功耗、内存和稳定性后应当最大化供给;
- 做多少事主要是从软件角度触发,聚焦于业务逻辑的优化,以及整个系统的优化,不执行冗余代码,尽可能不阻塞关键路径和抢占其资源。
- 达成目标:快且流畅,减少迟滞、波动(时快时慢)和不连贯(丢帧)给用户带来的心理落差。总结三个字就是:快、稳、省。
- 优化建议:有全局观,抓主要矛盾,综合取舍。
二、性能关键技术
1. CPU调度
1.1 调度单位
线程是Linux的最小调度单位,而资源是以进程为单位进行分配和管理,包括程序(program text)、数据(data )、文件(open file)等,这些资源由同一进程下的线程共享。
1.2 线程状态
分析性能问题时,主要关注下面几种状态:
- Running(R): 线程在正常执行代码逻辑
- Runnable(R): 可执行状态,等待调度,如果长时间调度不到,说明CPU繁忙
- Sleeping(S): 休眠,一般是在等待事件驱动
- Uninterruptible Sleep(D): 不可中断的休眠,需要看Args的描述来确定当时的状态
- Uninterruptible Sleep - Block I/O(D): IO阻塞
1.3 线程优先级
Linux线程优先级
Linux线程优先级的范围是 0 ~ 139,值越小,优先级越高。userspace线程优先级的范围是 100 ~ 139,默认创建的线程优先级是120,对应的nice值是0,nice值的范围是 -20 ~ 19,对应的优先级是 100 ~ 139。只有内核线程才支持低于100的优先级,优先级低于100的线程称为RT级线程。
前面值的定义可以从如下链接 https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/include/linux/sched/prio.h?q=linux%2Fsched%2Fprio.h 获取。
Android的Process类封装了线程优先级设置接口,范围是 -20 ~ 19,跟Linux的nice值一致。Android预定义了一些优先级给系统使用。代码详细见如下:
https://cs.android.com/android/platform/superproject/+/android14-qpr3-release:frameworks/base/core/java/android/os/Process.java?q=java%2Fandroid%2Fos%2FProcess.java
线程优先级 | nice值 | 解释 |
THREAD_PRIORITY_LOWEST | 19 | 最低优先级 |
THREAD_PRIORITY_BACKGROUND | 10 | 后台 |
THREAD_PRIORITY_LESS_FAVORABLE | 1 | 比默认略低 |
THREAD_PRIORITY_DEFAULT | 0 | 默认 |
THREAD_PRIORITY_MORE_FAVORABLE | -1 | 比默认略高 |
THREAD_PRIORITY_FOREGROUND | -2 | 前台 |
THREAD_PRIORITY_DISPLAY | -4 | 显示相关 |
THREAD_PRIORITY_URGENT_DISPLAY | -8 | 显示(更为重要),input事件 |
THREAD_PRIORITY_AUDIO | -16 | 音频相关 |
THREAD_PRIORITY_URGENT_AUDIO | -19 | 音频(更为重要) |
Java线程优先级
Java标准接口是通过Thread类设置优先级,范围为1 ~ 10。代码详细见如下所示:
https://cs.android.com/android/platform/superproject/main/+/main:libcore/openjdk_java_files.bp;l=250?q=main%2Fjava%2Fjava%2Flang%2FThread.java
Java优先级最终也要通过系统调用来设置进程的NICE值来调整进程的优先级的。代码如下所示:
https://cs.android.com/android/platform/superproject/+/android14-qpr3-release:art/runtime/thread.cc?q=art%2Fruntime%2Fthread.cc
static const int kNiceValues[art::palette::kNumManagedThreadPriorities] = {
ANDROID_PRIORITY_LOWEST, // 1 (MIN_PRIORITY)
ANDROID_PRIORITY_BACKGROUND + 6,
ANDROID_PRIORITY_BACKGROUND + 3,
ANDROID_PRIORITY_BACKGROUND,
ANDROID_PRIORITY_NORMAL, // 5 (NORM_PRIORITY)
ANDROID_PRIORITY_NORMAL - 2,
ANDROID_PRIORITY_NORMAL - 4,
ANDROID_PRIORITY_URGENT_DISPLAY + 3,
ANDROID_PRIORITY_URGENT_DISPLAY + 2,
ANDROID_PRIORITY_URGENT_DISPLAY // 10 (MAX_PRIORITY)
};
palette_status_t PaletteSchedSetPriority(int32_t tid, int32_t managed_priority) {
if (managed_priority < art::palette::kMinManagedThreadPriority ||
managed_priority > art::palette::kMaxManagedThreadPriority) {
return PALETTE_STATUS_INVALID_ARGUMENT;
}
int new_nice = kNiceValues[managed_priority - art::palette::kMinManagedThreadPriority];
int curr_nice = getpriority(PRIO_PROCESS, tid);
if (curr_nice == new_nice) {
return PALETTE_STATUS_OK;
}
if (new_nice >= ANDROID_PRIORITY_BACKGROUND) {
SetTaskProfiles(tid, {"SCHED_SP_BACKGROUND"}, true);
} else if (curr_nice >= ANDROID_PRIORITY_BACKGROUND) {
SchedPolicy policy;
// Change to the sched policy group of the process.
if (get_sched_policy(getpid(), &policy) != 0) {
policy = SP_FOREGROUND;
}
SetTaskProfiles(tid, {get_sched_policy_profile_name(policy)}, true);
}
if (setpriority(PRIO_PROCESS, tid, new_nice) != 0) {
return PALETTE_STATUS_CHECK_ERRNO;
}
return PALETTE_STATUS_OK;
}
- MAX_PRIORITY相当于android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY,值为10;
- MIN_PRIORITY相当于android.os.Process.THREAD_PRIORITY_LOWEST,值为0;
- NORM_PRIORITY相当于android.os.Process.THREAD_PRIORITY_DEFAULT,值为5。
1.4 CPU调度策略
1.4.1 调度器
高通从Kernel 4.9之后使用EAS (Energy Aware Scheduling)作为CPU调度器。EAS针对异构 CPU 架构(Arm big.LITTLE)设计,其调度算法基于 CFS 任务唤醒平衡代码,当时引入能量模型( EM)在调度任务时选择节能目标CPU,在保证性能的前提下尽可能地降低功耗。
1.4.2 CPUFreq Governor
跟EAS配合动态调整CPU频点。对性能影响较大,需要了解governor提供的参数配置进行tune来达到较佳效果。
- Walt
- Schedutil
1.4.3 CGroup
CGroups是control groups的缩写,是Linux内核提供的一种可以限制、记录、隔离进程组(process groups)所使用的物理资源(cpu,memory,IO等等)的机制。CGroups提供了以下功能:
- 限制进程组可以使用的资源数量(Resource limiting )。比如:memory子系统可以为进程组设定一个memory使用上限,一旦进程组使用的内存达到限额再申请内存,就会出发OOM(out of memory)。
- 进程组的优先级控制(Prioritization )。比如:可以使用cpu子系统为某个进程组分配特定cpu share。
- 记录进程组使用的资源数量(Accounting )。比如:可以使用cpuacct子系统记录某个进程组使用的cpu时间
- 进程组隔离(Isolation)。比如:使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。
- 进程组控制(Control)。比如:使用freezer子系统可以将进程组挂起和恢复。
根据谷歌文档(https://source.android.com/devices/tech/perf/cgroups), Android 9 及更低版本,只能通过init.rc初始化脚本创建及挂载cgroup,而Android 10 及更高版在early-init阶段挂载,在cgroups.json文件中配置cgroup组及挂载路径属性等。
cgroups其挂载也可以用下面命令查看。
thor:/ mount -t cgroup
none on /dev/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
none on /dev/cpuctl type cgroup (rw,nosuid,nodev,noexec,relatime,cpu)
none on /dev/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset,noprefix,release_agent=/sbin/cpuset_release_agent)
none on /dev/memcg type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
查看进程的cgroup配置,如camera provider:
thor:/ cat /proc/$(pidof vendor.qti.camera.provider@2.7-service_64)/cgroup
4:memory:/camera/provider
3:cpuset:/camera-daemon
2:cpu:/foreground
1:blkio:/
0::/uid_1047/pid_1197
- cpuset
文件节点 | 作用 | |
tasks | 列举绑定到某个 cgroup的所有线程程ID,将线程ID写入此文件会将线程移至该cgroup。 | cgroup通用 |
cgroup.procs | cgroup中的进程ID的列表,将进程ID写入此文件会将进程移至该cgroup。 | cgroup通用 |
notify_on_release | flag 文件: :填 0 或 1,表示是否在 cgroup 中最后一个 task 退出时通知运行release agent,默认情况下是 0,表示不运行 | cgroup通用 |
release_agent | 指定 release agent 执行脚本的文件路径(此文件仅在顶级cgroup中存在),在这个脚本通常用于自动化umount无用的 cgroup | cgroup通用 |
cgroup.clone_children | 该标志仅影响cpuset控制器。 如果在cgroup中启用了 clone_children 标志(1),则新的cpuset cgroup将在初始化期间从父级复制其配置。 | cpuset特有 |
cpuset.cpus | 限制一组进程所能使用的cpu, 供用户进行设置 | |
cpuset.cpu_exclusive | flag:设置为1时,说明独占指定cpus,默认为0 独占cpu的意思是,如果某个cpuset是独占的,那么该cpuset中的cpu是不能被该cpuset的兄弟cpuset共享的,当然该cpuset中的cpu是能够和该cpuset的祖先cpuset和子孙cpuset共享的。 注意:设置exclusive时,需要其父节点也为exclusive。否则会设置失败。所以,最终还是说明cpuset的独占,就是从根节点开始,就已经独占了。 | |
cpuset.effective_cpus | 显示实际进程可以使用的cpu | |
sched_load_balance |
| |
sched_relax_domain_level | 可设置的值为 | -1 : no request. use system default or follow request of others. 0 : no search. 1 : search siblings (hyperthreads in a core). 2 : search cores in a package. 3 : search cpus in a node [= system wide on non-NUMA system] 4 : search nodes in a chunk of node [on NUMA system] 5 : search system wide [on NUMA system] |
mems相关节点 | android只有一个内存节点,用不到 |
- cpuctl
文件节点 | 作用 | |
tasks | 列举绑定到某个 cgroup的所有线程程ID,将线程ID写入此文件会将线程移至该cgroup | |
cgroup.clone_children | ||
cgroup.procs | ||
cpu.shares | 限制cgroup的cpu相对使用比例,值越大拿到的CPU资源越多 | |
cpu.uclamp.latency_sensitive | ||
cpu.uclamp.max | ||
cpu.uclamp.min | ||
notify_on_release |
- 任务配置文件(Task profiles file)
Android 10之后使用task_profiles.json(https://source.n.xiaomi.com/opengrok-s/xref/pangu_8450-s-combine/system/core/libprocessgroup/profiles/task_profiles.json)作为任务配置文件,提供了一种抽象概念将必需的功能与该功能的实现详情分离。Android 框架使用 SetTaskProfiles 和 SetProcessProfiles API,按照 task_profiles.json 文件中的描述将任务配置文件应用于进程或线程(这些 API 是 Android 11 及更高版本所独有的)。为了实现向后兼容,旧版函数 set_cpuset_policy、set_sched_policy 和 get_sched_policy 提供相同的 API 和功能,但其实现已修改为使用任务配置文件。对于新的用例,AOSP 建议使用新的任务配置文件 API,而不是旧版 set_sched_policy 函数。
- 组优先级
组优先级Process.java | 优先级策略 SchedPolicy | 对应cpuset group级别 sched_policy.cpp |
THREAD_GROUP_BACKGROUND 0 | SP_BACKGROUND | /dev/cpuset/background/tasks |
THREAD_GROUP_FOREGROUND 1 THREAD_GROUP_AUDIO_APP 3 THREAD_GROUP_AUDIO_SYS 4 | SP_FOREGROUND SP_AUDIO_APP SP_AUDIO_SYS | /dev/cpuset/foreground/tasks |
THREAD_GROUP_TOP_APP 5 | SP_TOP_APP | /dev/cpuset/top-app/tasks |
THREAD_GROUP_SYSTEM 2 | SP_SYSTEM | /dev/cpuset/system-background/tasks |
THREAD_GROUP_RESTRICTED 7 | SP_RESTRICTED | /dev/cpuset/restricted/tasks |
THREAD_GROUP_RT_APP 6 | SP_RT_APP | N/A |
AMS在调度过程根据优先级进行分组,同时,进程也可以使用api设置资源使用优先级以及分组。如果有权限,还可以把task id写入cpuset中。常见的是APP使用AsyncTask类,在doInBackground中执行优先级较低的代码,其实就是把该task分到BACKGROUND的组。
可以利用cgroup机制,改善了provider进程出现长时间runnable的问题。
总的来时,CPU调度的优化最终会影响task的vruntime(虚拟运行时间),vruntime记录了一个task到当前时刻为止执行的总时间(需要以task总数n进行归一化,并且根据task的优先级进行加权)。vruntime越大,说明该进程运行的越久,所以被调度的可能性就越小。
1.5 PerfLock
//后续有文章进行详细介绍
1.6 进程冻结
//后续有文章进行详细介绍
1.7 LMKD
1.7.1 水线
- 水线配置
可以使用下面命令查看内存水线设置。
thor:/ getprop sys.lmk.minfree_levels
18432:0,23040:100,27648:200,32256:250,55296:900,80640:950
- 双水线
相机切换到前台时会更新水线设置,适当降低内存回收力度。
1.7.2 ADJ
ADJ值范围为-1000 - 1000,通过cat /proc/[pid]/oom_score_adj查看。系统在剩余内存低于水线阈值时,为了维护正在运行的进程,杀掉优先级比较低(adj值比较高)的其他进程。
ADJ级别 | 取值 | 含义 |
NATIVE_ADJ | -1000 | native进程 |
SYSTEM_ADJ | -900 | 仅指system_server进程 |
PERSISTENT_PROC_ADJ | -800 | 系统persistent进程 |
PERSISTENT_SERVICE_ADJ | -700 | 关联着系统或persistent进程 |
FOREGROUND_APP_ADJ | 0 | 前台进程 |
VISIBLE_APP_ADJ | 100 | 可见进程 |
PERCEPTIBLE_APP_ADJ | 200 | 可感知进程,比如后台音乐播放 |
BACKUP_APP_ADJ | 300 | 备份进程 |
HEAVY_WEIGHT_APP_ADJ | 400 | 重量级进程 |
SERVICE_ADJ | 500 | 服务进程(A list中的service) |
HOME_APP_ADJ | 600 | Home进程 |
PREVIOUS_APP_ADJ | 700 | 上一个进程 |
SERVICE_B_ADJ | 800 | B List中的Service |
CACHED_APP_MIN_ADJ | 900 | 不可见进程的adj最小值 |
CACHED_APP_MAX_ADJ | 906 | 不可见进程或不含任何活动的应用组件的empty进程的adj最大值 |
1.8 温控
1.8.1 温控配置和策略
1.8.2 thermald
//后续有时间详细介绍
2. IO调度
IO调度策略、IO block和IO优化(prefetch等),这块内容比较多,后续有空会详细介绍。
3. 内存分配
3.1 内存分配器
- scudo
- jemalloc
- Android Extensions(https://android.googlesource.com/platform/bionic/+/HEAD/docs/native_allocator.md)
mallopt(M_DECAY_TIME, 1)和mallopt(M_PURGE, 0),其中mallopt(M_PURGE, 0)耗时长问题。
3.2 内存压缩和回收
- ZRAM
- 内存回收机制
- 内存碎片整理
https://blog.csdn.net/buhui912/article/details/115895277
3.3 Camera内存
- imagebuffer
- metadata pool
- ion/no-ion
- HALBufferManager
https://source.android.com/devices/camera/buffer-management-api: Android对HALBufferManager的定义
Camera HAL3 Buffer Management详解
Camera Service buffer与HAL交互
CameraService Buffer如何与HAL交互
高通StreamBufferManager学习
4. 锁机制
- Java锁
- pthread mutex、condition
- futex
- FileLock
5. IPC
- binder/aidl/hidl:线程优先级继承、oneway
- broadcast的性能问题
- local socket
6. Graphics Framework
https://blog.csdn.net/u012365926/article/details/114921456
- SurfaceFlinger: composer、vsync
- BufferQueue: producer、consumer、fence、Gralloc
- GLRender
界定方法
7. HW Performance
- GPU/DDR/DSP/HTP/ISP/NCS sensor/videoEncoder(OMX)/BUS(CCI&MIPI):clock、loading、usage
- sensor node (ois,actuator、otp)、IPE、IFE
- sensor打分:
- OIS带flash:M3的OIS带有flash,启动速度可以从L1的120ms优化到5ms
- I2C总线合理挂载slave:避免同时使用的硬件发生抢占,提高camera工作是多给CCI总线能做到并行传输,以此提高性能
三、性能分析工具
1. Perfetto/Systrace/Atrace
https://perfetto.dev/docs/#/?id=perfetto-performance-instrumentation-and-tracing
https://perfetto.dev/docs/data-sources/memory-counters
https://perfetto.dev/docs/quickstart/android-tracing
2. Simpleperf & 火焰图
- simpleperf指令:record和report等;
- android源码丰富的解析脚本.
-
FlameGraph和红蓝火焰图
3. Android Profiler
https://developer.android.com/studio/profile/android-profiler
- 集成在AndroidStudio中;
- 需要有APP的源码;
- 可以抓java和native调用堆栈。
4. ftrace
- android官网:https://source.android.google.cn/devices/tech/debug/ftrace?hl=zh-cn
- 查看支持的events:cat /sys/kernel/tracing/available_events
- 设置events: echo "[event]" > /sys/kernel/tracing/set_event
5. Linux perf工具
https://blog.csdn.net/yiyeguzhou100/article/details/102809576
6. eBPF
Android中的eBPF程序简介-CSDN博客
四、Camera场景性能分析
1.启动
1.1 冷启动
1.1.1 关键路径
//关键流程分析
1.1.2 流程分析
1.1.2.1 APP进程保活
- 保活机制FastRestart
- 相机被kill掉后3秒多能被保活机制拉起
02-04 20:17:03.192 4312 4312 D RecentsContainer: removeTask: [id=105 stackId=0 windowingMode=1 user=0 lastActiveTime=318591997, component=ComponentInfo{com.a
ndroid.camera/com.android.camera.Camera}] 相机
02-04 20:17:03.214 2226 5341 D WindowProcessUtils: remove task: Task{9fdb79 #105 type=standard A=10089:com.android.camera U=0 visible=false mode=fullscreen t
ranslucent=true sz=1}
02-04 20:17:03.215 2226 5341 I SplashScreenServiceDelegate: None for com.android.camera
02-04 20:17:03.241 2226 5341 I ProcessManager: OneKeyClean: kill com.android.camera Adj=900 State=16
02-04 20:17:03.241 2226 5341 I ActivityManager: Killing 23899:com.android.camera/u0a89 (adj 900): OneKeyClean
02-04 20:17:03.242 2226 5341 D PerfImpl: perfProcessKillBoost: com.android.camera, 23899, 0
02-04 20:17:03.533 2226 2260 W UsageStatsService: Unexpected activity event reported! (com.android.camera/com.android.camera.Camera event : 23 instanceId : 2
31437437)
02-04 20:17:05.554 2226 2298 D Boost : hostingType=FastRestart, hostingName=com.android.camera, callerPackage=android, isSystem=true, isBoostNeeded=false.
02-04 20:17:05.554 2226 2298 I ActivityManager: Start proc 27596:com.android.camera/u0a89 for FastRestart com.android.camera caller=android
02-04 20:17:05.596 5231 11184 W MQSService: CallerName:com.android.camera,calling Uid:10089
02-04 20:17:05.596 5231 11184 D MQSService: registerApplicationScoutThread pid = 27596, packageName = com.android.camera
02-04 20:17:05.709 27596 27596 V GraphicsEnvironment: ANGLE Developer option for 'com.android.camera' set to: 'default'
02-04 20:17:05.712 27596 27596 I ForceDarkHelperStubImpl: initialize for com.android.camera , ForceDarkOrigin
02-04 20:17:05.800 27596 27596 D CAM_BoostFrameworkImpl: stopBoost: com.android.camera.CameraAppImpl.attachBaseContext:42
02-04 20:17:05.838 27596 27596 D CAM_BoostFrameworkImpl: stopBoost: com.android.camera.CameraAppImpl.onCreate:12
02-04 20:17:05.845 27596 27596 D MiCameraAlgo: init: application file path to algorithm lib: /data/user/0/com.android.camera/files
02-04 20:17:05.860 27596 27596 D CAM_AlgoConnector: onServiceConnected: ComponentInfo{com.android.camera/com.android.camera.LocalParallelService}, binder = com
- Application启动
大致流程:ZygoteInit->ActivityThreadMain->bindApplication->serviceCreate->serviceBind
1.1.2.2 启动Trace分析
- 关键trace点
- iq
- startActivityInner
- activityStart/activityResume ...
HAL3: RequestTrace/HAL3ProcessCapture/ProcessRequest [request id]/ProcessRequestIdDone/HAL3ProcessCaptureResult ...
1.1.3 关键点
- CPU频率设置
- 关键线程runnable和sleep情况
1)running使用trace跟火焰图分析
2)sleep开hal的sync
3)runnable看trace中CPU的抢占
- APP关键操作的衔接
- HAL pipeline
1.2 热启动
1.2.1 关键路径
1.2.2 流程分析
1.2.3 关键点
1.2 预览卡顿
生产者和消费者
2. 模式切换
3. Lens切换
4. 拍照
5. 录像
五、优化点
1. 异步化
2. 提前和延后
避免集中化
3. IO
- pin
- 预加载
4. 调度优化
google相关内容
5. 业务逻辑优化
- 线程优先级
- 绑核
- 调度参数调优
- vruntime补偿
- EarlyPCR
- 提前active pipeline
- offline feature延后create
- 等等
小结
以上简单总结了一下camera性能优化基础的关键点内容,最重要的其实就是2大类知识。
1.熟练掌握 Linux/Android系统 进程调度,内存管理,文件系统,网络通信等知识点
2.熟练掌握camera系统业务知识,要非常熟悉camera业务关键流程(open/configure/request/result/close等等)。