Android 8.0 中包含用于测试吞吐量和延迟的 binder 和 hwbinder 性能测试。虽然有很多场景都可用于检测可察觉的性能问题,但运行此类场景可能会比较耗时,而且相应结果通常要到集成完系统之后才可获得。借助 Android 8.0 中提供的性能测试,您可更轻松地在开发过程中进行测试、及早发现严重问题以及改善用户体验。
性能测试包括以下四个类别:
- binder 吞吐量(在
system/libhwbinder/vts/performance/Benchmark_binder.cpp
中提供) - binder 延迟时间(在
frameworks/native/libs/binder/tests/schd-dbg.cpp
中提供) - hwbinder 吞吐量(在
system/libhwbinder/vts/performance/Benchmark.cpp
中提供) - hwbinder 延迟时间(在
system/libhwbinder/vts/performance/Latency.cpp
中提供)
关于 binder 和 hwbinder
binder 和 hwbinder 都是 Android 进程间通信 (IPC) 基础架构,它们共用同一个 Linux 驱动程序,但在本质上具有以下不同之处:
方面 | binder | hwbinder |
---|---|---|
用途 | 为框架提供通用型 IPC 方案 | 与硬件通信 |
属性 | 专门针对 Android 框架使用情景做了优化 | 开销少,延迟低 |
更改前台/后台的调度策略 | 是 | 否 |
传递参数 | 使用由 Parcel 对象支持的序列化 | 使用分散缓冲区,避免因复制 Parcel 序列化所需数据而产生开销 |
继承优先级 | 否 | 是 |
binder 和 hwbinder 进程
Systrace 可视化工具会以如下方式显示事务:
在上述示例中:
- 四 (4) 个 schd-dbg 进程是客户端进程。
- 四 (4) 个 binder 进程是服务器进程(名称以 Binder 开头,且以序列号结尾)。
- 每个客户端进程始终都会与某个服务器进程(供其客户端专用)配对。
- 所有客户端-服务器进程对均由内核同时单独调度。
在 CPU 1 中,操作系统内核会运行客户端以发出请求。然后,它会尽可能地使用同一 CPU 唤醒服务器进程、处理请求,并会在处理完请求后切换回原环境。
吞吐量和延迟
在理想的事务中,由于客户端进程和服务器进程可以无缝切换,吞吐量测试和延迟测试在生成的信息方面不会有很大差异。不过,如果操作系统内核在处理来自硬件的中断请求 (IRQ)、等待锁定,或只是选择不立即处理信息,则可能会形成延迟气泡。
吞吐量测试会生成很多具有不同负载量的事务,因此可以很好地估算常规事务时间(在最理想的情况下)以及 binder 可达到的最大吞吐量。
相比之下,延迟测试不会对负载执行任何操作,以最大限度地减少常规事务时间。我们可以利用事务时间来估算 binder 开销、对最坏的情况进行信息统计,并计算那些在延迟方面达到指定截止时间的事务所占的比例。
处理优先级倒置
如果优先级较高的线程在逻辑上需要等待优先级较低的线程,就会出现优先级倒置的问题。实时 (RT) 应用存在优先级倒置问题:
如果某个线程使用 Linux 完全公平的调度程序 (CFS) 进行调度,那么即使其他线程的优先级较高,该线程也总会有机会运行。因此,采用 CFS 调度的应用会将优先级倒置作为一种预期行为(而非问题)来处理。不过,如果 Android 框架需要使用 RT 调度,以保证优先级较高的线程的权限,则必须先解决优先级倒置问题。
binder 事务中的优先级倒置示例(RT 线程在等待 binder 线程提供服务时在逻辑上被其他 CFS 线程阻塞):
为了避免出现阻塞情况,您可以在 binder 线程处理来自 RT 客户端的请求时,使用优先级继承暂时将 binder 线程升级到 RT 线程。请注意,RT 调度的资源有限,应谨慎使用。在具有 n 个 CPU 的系统中,当前 RT 线程的数量上限也为 n;如果所有 CPU 均已被其他 RT 线程占用,那么超出的那些 RT 线程可能需要等待(因此将超出其截止时间)。
要解决所有可能出现的优先级倒置问题,您可以针对 binder 和 hwbinder 使用优先级继承。不过,由于 binder 广泛用于整个系统,因此为 binder 事务启用优先级继承可能会使系统中的 RT 线程数超过其所能处理的线程数。
运行吞吐量测试
吞吐量测试是针对 binder/hwbinder 事务吞吐量而运行的。在未过载的系统中,延迟气泡很少,而且只要迭代的次数足够多,就可以消除其影响。
- binder 吞吐量测试位于
system/libhwbinder/vts/performance/Benchmark_binder.cpp
下。 - hwbinder 吞吐量测试位于
system/libhwbinder/vts/performance/Benchmark.cpp
下。
测试结果
针对使用不同负载量的事务的吞吐量测试结果示例:
Benchmark Time CPU Iterations --------------------------------------------------------------------- BM_sendVec_binderize/4 70302 ns 32820 ns 21054 BM_sendVec_binderize/8 69974 ns 32700 ns 21296 BM_sendVec_binderize/16 70079 ns 32750 ns 21365 BM_sendVec_binderize/32 69907 ns 32686 ns 21310 BM_sendVec_binderize/64 70338 ns 32810 ns 21398 BM_sendVec_binderize/128 70012 ns 32768 ns 21377 BM_sendVec_binderize/256 69836 ns 32740 ns 21329 BM_sendVec_binderize/512 69986 ns 32830 ns 21296 BM_sendVec_binderize/1024 69714 ns 32757 ns 21319 BM_sendVec_binderize/2k 75002 ns 34520 ns 20305 BM_sendVec_binderize/4k 81955 ns 39116 ns 17895 BM_sendVec_binderize/8k 95316 ns 45710 ns 15350 BM_sendVec_binderize/16k 112751 ns 54417 ns 12679 BM_sendVec_binderize/32k 146642 ns 71339 ns 9901 BM_sendVec_binderize/64k 214796 ns 104665 ns 6495
- 时间表示实时测量的往返延迟时间。
- CPU 表示调度 CPU 以进行测试的累计时间。
- 迭代表示执行测试函数的次数。
以 8 字节的载荷为例:
BM_sendVec_binderize/8 69974 ns 32700 ns 21296
… binder 可以达到的最大吞吐量的计算公式为:
8 字节负载的最大吞吐量 = (8 * 21296)/69974 ~= 2.423 b/ns ~= 2.268 Gb/s
测试选项
如需获得 .json 格式的结果,请使用 --benchmark_format=json
参数运行测试:
libhwbinder_benchmark --benchmark_format=json
{
"context": {
"date": "2017-05-17 08:32:47",
"num_cpus": 4,
"mhz_per_cpu": 19,
"cpu_scaling_enabled": true,
"library_build_type": "release"
},
"benchmarks": [
{
"name": "BM_sendVec_binderize/4",
"iterations": 32342,
"real_time": 47809,
"cpu_time": 21906,
"time_unit": "ns"
},
….
}
运行延迟时间测试
延迟测试可测量以下事项所花费的时间:客户端开始初始化事务、切换到服务器进程进行处理,以及接收结果。此外,该测试还会查找可对事务延迟产生负面影响的已知不良调度程序行为,例如,调度程序不支持优先级继承或不接受同步标志。
- binder 延迟测试位于
frameworks/native/libs/binder/tests/schd-dbg.cpp
下。 - hwbinder 延迟测试位于
system/libhwbinder/vts/performance/Latency.cpp
下。
测试结果
测试结果(.json 格式)将显示有关平均/最佳/最差延迟情况以及超出截止时间的次数的统计信息。
测试选项
延迟测试采用以下选项:
命令 | 说明 |
---|---|
-i value |
指定迭代次数。 |
-pair value |
指定进程对的数量。 |
-deadline_us 2500 |
指定截止时间(以微秒为单位)。 |
-v |
获取详细的(调试)输出。 |
-trace |
在达到截止时间时暂停跟踪。 |
以下几个部分会详细介绍每个选项,说明相关使用情况,并提供示例结果。
指定迭代
具有大量迭代次数并停用了详细输出功能的结果示例:
libhwbinder_latency -i 5000 -pair 3
{
"cfg":{"pair":3,"iterations":5000,"deadline_us":2500},
"P0":{"SYNC":"GOOD","S":9352,"I":10000,"R":0.9352,
"other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, "meetR":0.9996},
"fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, "meetR":1}
},
"P1":{"SYNC":"GOOD","S":9334,"I":10000,"R":0.9334,
"other_ms":{ "avg":0.19, "wst":2.9 , "bst":0.055, "miss":2, "meetR":0.9996},
"fifo_ms": { "avg":0.16, "wst":3.1 , "bst":0.066, "miss":1, "meetR":0.9998}
},
"P2":{"SYNC":"GOOD","S":9369,"I":10000,"R":0.9369,
"other_ms":{ "avg":0.19, "wst":4.8 , "bst":0.055, "miss":6, "meetR":0.9988},
"fifo_ms": { "avg":0.15, "wst":1.8 , "bst":0.067, "miss":0, "meetR":1}
},
"inheritance": "PASS"
}
这些测试结果会显示以下信息:
"pair":3
- 创建一个客户端和服务器对。
"iterations": 5000
- 包括 5000 次迭代。
"deadline_us":2500
- 截止时间为 2500 微秒(2.5 毫秒);大多数事务都应达到该值。
"I": 10000
- 单次测试迭代包括两 (2) 项事务:
- 一项按照普通优先级 (
CFS other
) 处理的事务 - 一项按照实时优先级 (
RT-fifo
) 处理的事务
- 一项按照普通优先级 (
"S": 9352
- 9352 项事务会在同一个 CPU 中进行同步。
"R": 0.9352
- 表示客户端和服务器在同一个 CPU 中一起同步的比例。
"other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, "meetR":0.9996}
- 由普通优先级调用程序分发的所有事务的平均 (
avg
)、最差 (wst
) 和最佳 (bst
) 情况。两项事务miss
截止时间,使得达标率为 (meetR
) 0.9996。 "fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, "meetR":1}
- 类似于
other_ms
,但适用于由具有rt_fifo
优先级的客户端分发的事务。有可能(但并非必须)fifo_ms
的结果优于other_ms
,avg
和wst
值较低且meetR
较高(在后台加载时,这种差异甚至会更明显)。
注意:后台负荷可能会影响延迟测试中的吞吐量结果和 other_ms
元组。只要后台负荷的优先级低于 RT-fifo
,就可能只有 fifo_ms
会显示类似的结果。
指定对值
每个客户端进程都会与其专用的服务器进程配对,且每一对都可能会独立调度到任何 CPU。不过,只要 SYNC 标志是 honor
,事务期间应该就不会出现 CPU 迁移的情况。
确保系统没有过载!虽然过载系统中延迟较高是正常现象,但是针对过载系统的测试结果并不能提供有用的信息。如需测试压力较高的系统,请使用 -pair
#cpu-1
(或谨慎使用 -pair #cpu
)。同时使用 n > #cpu
和 -pair n
进行测试会导致系统过载,并生成无用信息。
指定截止时间值
经过大量用户场景测试(在合格产品上运行延迟测试),我们决定将 2.5 毫秒定为需要满足的截止时间要求。对于具有更高要求的新应用(如每秒 1000 张照片),此截止时间值将发生变化。
指定详细输出
使用 -v
选项显示详细输出。示例:
libhwbinder_latency -i 1 -v
-------------------------------------------------- service pid: 8674 tid: 8674 cpu: 1 SCHED_OTHER 0-------------------------------------------------- main pid: 8673 tid: 8673 cpu: 1 -------------------------------------------------- client pid: 8677 tid: 8677 cpu: 0 SCHED_OTHER 0-------------------------------------------------- fifo-caller pid: 8677 tid: 8678 cpu: 0 SCHED_FIFO 99 -------------------------------------------------- hwbinder pid: 8674 tid: 8676 cpu: 0 ??? 99-------------------------------------------------- other-caller pid: 8677 tid: 8677 cpu: 0 SCHED_OTHER 0 -------------------------------------------------- hwbinder pid: 8674 tid: 8676 cpu: 0 SCHED_OTHER 0
- 该服务线程创建时的优先级为
SCHED_OTHER
,并以pid 8674
在CPU:1
中运行。 - 随后,由
fifo-caller
启动第一个事务。为处理该事务,hwbinder 会将服务器 (pid: 8674 tid: 8676
) 的优先级升级到 99,并使用瞬态调度类别(输出为???
)对其进行标记。接下来,调度程序会将服务器进程置于CPU:0
中,以运行该进程并让它在同一 CPU 中与其客户端进行同步。 - 第二个事务调用程序的优先级为
SCHED_OTHER
。服务器自行降级并为优先级为SCHED_OTHER
的调用程序提供服务。
使用跟踪记录进行调试
您可以指定 -trace
选项来调试延迟问题。使用该选项时,延迟测试会在检测到不良延迟时停止跟踪日志记录。示例:
atrace --async_start -b 8000 -c sched idle workq binder_driver sync freq
libhwbinder_latency -deadline_us 50000 -trace -i 50000 -pair 3
deadline triggered: halt ∓ stop trace log:/sys/kernel/debug/tracing/trace
以下组件可能会影响延迟:
- Android 编译模式。Eng 模式通常比 Userdebug 模式速度慢。
- 框架。框架服务如何使用
ioctl
来配置 binder? - binder 驱动程序。驱动程序是否支持精细锁定?驱动程序是否包含所有性能调整补丁程序?
- 内核版本。内核的实时性能越好,结果就越好。
- 内核配置。内核配置是否包含
DEBUG_PREEMPT
和DEBUG_SPIN_LOCK
等DEBUG
配置? - 内核调度程序。内核中是否具有 Energy-Aware 调度程序 (EAS) 或异构多处理 (HMP) 调度程序?有没有内核驱动程序(
cpu-freq
驱动程序、cpu-idle
驱动程序、cpu-hotplug
等)会影响调度程序?