背景
做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_LOWEST19最低优先级THREAD_PRIORITY_BACKGROUND10后台THREAD_PRIORITY_LESS_FAVORABLE1比默认略低THREAD_PRIORITY_DEFAULT0默认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。代码详细见如下所示:
Java优先级最终也要通过系统调用来设置进程的NICE值来调整进程的优先级的。代码如下所示:
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 [email protected]_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
flag
:默认为值
1
,表示进程会在该
cpuset
中允许的
cpu
上进行负载均衡
sched_relax_domain_level
可设置的值为
-1
到一个较小的整数值,只有
sched_load_balance
为
1
时生效,值越大,表示负载均衡时查找的
cpu
范围越大
-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-1000native进程SYSTEM_ADJ-900仅指system_server进程PERSISTENT_PROC_ADJ-800系统persistent进程PERSISTENT_SERVICE_ADJ-700关联着系统或persistent进程FOREGROUND_APP_ADJ0前台进程VISIBLE_APP_ADJ100可见进程PERCEPTIBLE_APP_ADJ200可感知进程,比如后台音乐播放BACKUP_APP_ADJ300备份进程HEAVY_WEIGHT_APP_ADJ400重量级进程SERVICE_ADJ500服务进程(A list中的service)HOME_APP_ADJ600Home进程PREVIOUS_APP_ADJ700上一个进程SERVICE_B_ADJ800B List中的ServiceCACHED_APP_MIN_ADJ900不可见进程的adj最小值CACHED_APP_MAX_ADJ906不可见进程或不含任何活动的应用组件的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等等)。
版权归原作者 repinkply 所有, 如有侵权,请联系我们删除。