| <html devsite><head> |
| <title>性能测试</title> |
| <meta name="project_path" value="/_project.yaml"/> |
| <meta name="book_path" value="/_book.yaml"/> |
| </head> |
| <body> |
| <!-- |
| Copyright 2017 The Android Open Source Project |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| |
| <p>Android O 中包含用于测试吞吐量和延迟的 binder 和 hwbinder 性能测试。虽然有很多场景都可用于检测可察觉的性能问题,但运行此类场景可能会比较耗时,而且相应结果通常要到集成完系统之后才可获得。借助 Android O 中提供的性能测试,您可更轻松地在开发过程中进行测试、及早发现严重问题以及改善用户体验。</p> |
| |
| <p>性能测试包括以下四个类别:</p> |
| <ul> |
| <li>binder 吞吐量(在 <code>system/libhwbinder/vts/performance/Benchmark_binder.cpp</code> 中提供)</li> |
| <li>binder 延迟(在 <code>frameworks/native/libs/binder/tests/schd-dbg.cpp</code> 中提供)</li> |
| <li>hwbinder 吞吐量(在 <code>system/libhwbinder/vts/performance/Benchmark.cpp</code> 中提供)</li> |
| <li>hwbinder 延迟(在 <code>system/libhwbinder/vts/performance/Latency.cpp</code> 中提供)</li> |
| </ul> |
| |
| <h2 id="about">关于 binder 和 hwbinder</h2> |
| <p>binder 和 hwbinder 都是 Android 进程间通信 (IPC) 基础架构,它们共用同一个 Linux 驱动程序,但在本质上具有以下不同之处:</p> |
| |
| <table> |
| <tbody><tr> |
| <th>方面</th> |
| <th>binder</th> |
| <th>hwbinder</th> |
| </tr> |
| |
| <tr> |
| <td>用途</td> |
| <td>为框架提供通用型 IPC 方案</td> |
| <td>与硬件通信</td> |
| </tr> |
| |
| <tr> |
| <td>属性</td> |
| <td>专门针对 Android 框架使用情景做了优化</td> |
| <td>开销少,延迟低</td> |
| </tr> |
| |
| <tr> |
| <td>更改前台/后台的调度策略</td> |
| <td>会</td> |
| <td>不会</td> |
| </tr> |
| |
| <tr> |
| <td>传递参数</td> |
| <td>使用由 Parcel 对象支持的序列化</td> |
| <td>使用分散缓冲区,避免因复制 Parcel 序列化所需数据而产生开销</td> |
| </tr> |
| |
| <tr> |
| <td>继承优先级</td> |
| <td>不会</td> |
| <td>会</td> |
| </tr> |
| |
| </tbody></table> |
| |
| <h3 id="transactions">binder 和 hwbinder 进程</h3> |
| <p>Systrace 可视化工具会以如下方式显示事务:</p> |
| <img src="../images/treble_systrace_binder_processes.png"/> |
| <figcaption><strong>图 1.</strong> binder 进程的 Systrace 可视化。</figcaption> |
| |
| <p>在上述示例中:</p> |
| <ul> |
| <li>四 (4) 个 schd-dbg 进程是客户端进程。</li> |
| <li>四 (4) 个 binder 进程是服务器进程(名称以 <strong>Binder</strong> 开头,且以序列号结尾)。</li> |
| <li>每个客户端进程始终都会与某个服务器进程(供其客户端专用)配对。</li> |
| <li>所有客户端-服务器进程对均由内核同时单独调度。</li> |
| </ul> |
| |
| <p>在 CPU 1 中,操作系统内核会运行客户端以发出请求。然后,它会尽可能地使用同一 CPU 唤醒服务器进程、处理请求,并会在处理完请求后切换回原环境。</p> |
| |
| <h3 id="throughput-diffs">吞吐量和延迟</h3> |
| <p>在理想的事务中,由于客户端进程和服务器进程可以无缝切换,吞吐量测试和延迟测试在生成的信息方面不会有很大差异。不过,如果操作系统内核在处理来自硬件的中断请求 (IRQ)、等待锁定,或只是选择不立即处理信息,则可能会形成延迟气泡。</p> |
| |
| <img src="../images/treble_latency_bubble.png"/> |
| <figcaption><strong>图 2.</strong> 因吞吐量测试结果和延迟测试结果之间的差异而形成的延迟气泡。</figcaption> |
| |
| <p>吞吐量测试会生成很多具有不同有效负荷量的事务,因此可以很好地估算常规事务时间(在最理想的情况下)以及 binder 可达到的最大吞吐量。</p> |
| |
| <p>相比之下,延迟测试不会对有效负荷执行任何操作,以最大限度地减少常规事务时间。我们可以利用事务时间来估算 binder 开销、对最坏的情况进行信息统计,并计算那些在延迟方面达到指定截止时间的事务所占的比例。</p> |
| |
| <h3 id="priority-inversions">处理优先级倒置</h3> |
| <p>如果优先级较高的线程在逻辑上需要等待优先级较低的线程,就会出现优先级倒置的问题。实时 (RT) 应用存在优先级倒置问题:</p> |
| |
| <img src="../images/treble_priority_inv_rta.png"/> |
| <figcaption><strong>图 3.</strong> 实时应用中的优先级倒置。</figcaption> |
| |
| <p>如果某个线程使用 Linux 完全公平的调度程序 (CFS) 进行调度,则即使其他线程的优先级较高,该线程也总会有机会运行。因此,采用 CFS 调度的应用会将优先级倒置作为一种预期行为(而非问题)来处理。不过,如果 Android 框架需要使用 RT 调度,以保证优先级较高的线程的权限,则必须先解决优先级倒置问题。</p> |
| |
| <p>binder 事务中的优先级倒置示例(RT 线程在等待 binder 线程提供服务时在逻辑上被其他 CFS 线程阻塞):</p> |
| <img src="../images/treble_priority_inv_rta_blocked.png"/> |
| <figcaption><strong>图 4.</strong> 优先级倒置;被阻塞的实时线程。</figcaption> |
| |
| <p>要避免出现阻塞情况,您可以在 binder 线程处理来自 RT 客户端的请求时,使用优先级继承暂时将 binder 线程升级到 RT 线程。请注意,RT 调度的资源有限,应谨慎使用。在具有 N 个 CPU 的系统中,当前 RT 线程的数量上限也为 N;如果所有 CPU 均已被其他 RT 线程占用,则额外的 RT 线程可能需要等待(因此将超出其截止时间)。<em></em><em></em></p> |
| |
| <p>要解决所有可能出现的优先级倒置问题,您可以针对 binder 和 hwbinder 使用优先级继承。不过,由于 binder 广泛用于整个系统,因此为 binder 事务启用优先级继承可能会使系统中的 RT 线程数超过其所能处理的线程数。</p> |
| |
| <h2 id="throughput">运行吞吐量测试</h2> |
| <p>吞吐量测试是针对 binder/hwbinder 事务吞吐量而运行的。在未过载的系统中,延迟气泡很少,而且只要迭代的次数足够多,就可以消除其影响。</p> |
| |
| <ul> |
| <li><strong>binder</strong> 吞吐量测试位于 <code>system/libhwbinder/vts/performance/Benchmark_binder.cpp</code> 下。</li> |
| <li><strong>hwbinder</strong> 吞吐量测试位于 <code>system/libhwbinder/vts/performance/Benchmark.cpp</code> 下。</li> |
| </ul> |
| |
| <h3 id="throughput-results">测试结果</h3> |
| <p>针对使用不同有效负荷量的事务的吞吐量测试结果示例:</p> |
| |
| <pre class="prettyprint"> |
| 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 |
| </pre> |
| |
| <ul> |
| <li><strong>时间</strong>表示实时测量的往返延迟时间。 |
| </li> |
| <li><strong>CPU</strong> 表示调度 CPU 以进行测试的累计时间。</li> |
| <li><strong>迭代</strong>表示执行测试函数的次数。</li> |
| </ul> |
| |
| <p>以 8 字节的有效负荷为例:</p> |
| |
| <pre class="prettyprint"> |
| BM_sendVec_binderize/8 69974 ns 32700 ns 21296 |
| </pre> |
| <p>… binder 可以达到的最大吞吐量的计算公式为:</p> |
| <p><em>8 字节有效负荷的最大吞吐量 = (8 * 21296)/69974 ~= 2.423 b/ns ~= 2.268 Gb/s</em></p> |
| |
| <h3 id="throughput-options">测试选项</h3> |
| <p>要获得 .json 格式的结果,请使用 <code>--benchmark_format=json</code> 参数运行测试:</p> |
| |
| <pre class="prettyprint"> |
| <code class="devsite-terminal">libhwbinder_benchmark --benchmark_format=json</code> |
| { |
| "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" |
| }, |
| …. |
| } |
| </pre> |
| |
| <h2 id="latency">运行延迟测试</h2> |
| <p>延迟测试可测量以下事项所花费的时间:客户端开始初始化事务、切换到服务器进程进行处理,以及接收结果。此外,该测试还会查找可对事务延迟产生负面影响的已知不良调度程序行为,例如,调度程序不支持优先级继承或不接受同步标记。</p> |
| |
| <ul> |
| <li>binder 延迟测试位于 <code>frameworks/native/libs/binder/tests/schd-dbg.cpp</code> 下。</li> |
| <li>hwbinder 延迟测试位于 <code>system/libhwbinder/vts/performance/Latency.cpp</code> 下。</li> |
| </ul> |
| |
| <h3 id="latency-results">测试结果</h3> |
| <p>测试结果(.json 格式)将显示有关平均/最佳/最差延迟以及超出截止时间的次数的统计信息。</p> |
| |
| <h3 id="latency-options">测试选项</h3> |
| <p>延迟测试采用以下选项:</p> |
| |
| <table> |
| <tbody><tr> |
| <th>命令</th> |
| <th>说明</th> |
| </tr> |
| |
| <tr> |
| <td><code>-i <em>value</em></code></td> |
| <td>指定迭代次数。</td> |
| </tr> |
| |
| <tr> |
| <td><code>-pair <em>value</em></code></td> |
| <td>指定进程对的数量。</td> |
| </tr> |
| |
| <tr> |
| <td><code>-deadline_us 2500</code></td> |
| <td>指定截止时间(以微秒为单位)。</td> |
| </tr> |
| |
| <tr> |
| <td><code>-v</code></td> |
| <td>获取详细的(调试)输出。</td> |
| </tr> |
| |
| <tr> |
| <td><code>-trace</code></td> |
| <td>在达到截止时间时暂停跟踪。</td> |
| </tr> |
| |
| </tbody></table> |
| |
| <p>以下几个部分会详细介绍每个选项,说明相关使用情况,并提供示例结果。</p> |
| |
| <h4 id="iterations">指定迭代</h4> |
| <p>具有大量迭代次数并停用了详细输出功能的结果示例:</p> |
| |
| <pre class="prettyprint"> |
| <code class="devsite-terminal">libhwbinder_latency -i 5000 -pair 3</code> |
| { |
| "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" |
| } |
| </pre> |
| <p>这些测试结果会显示以下信息:</p> |
| |
| <dl> |
| <dt><strong><code>"pair":3</code></strong></dt> |
| <dd>创建一个客户端和服务器对。</dd> |
| |
| <dt><strong><code>"iterations": 5000</code></strong></dt> |
| <dd>包括 5000 次迭代。</dd> |
| |
| <dt><strong><code>"deadline_us":2500</code></strong></dt> |
| <dd>截止时间为 2500 微秒(2.5 毫秒);大多数事务都应达到该值。</dd> |
| |
| <dt><strong><code>"I": 10000</code></strong></dt> |
| <dd>单次测试迭代包括两 (2) 项事务:<ul> |
| <li>一项按照正常优先级 (<code>CFS other</code>) 处理的事务</li> |
| <li>一项按照实时优先级 (<code>RT-fifo</code>) 处理的事务</li> |
| </ul>5000 次迭代相当于共计 10000 项事务。</dd> |
| |
| <dt><strong><code>"S": 9352</code></strong></dt> |
| <dd>9352 项事务会在同一个 CPU 中进行同步。</dd> |
| |
| <dt><strong><code>"R": 0.9352</code></strong></dt> |
| <dd>表示客户端和服务器在同一个 CPU 中一起同步的比例。</dd> |
| |
| <dt><strong><code>"other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, |
| "meetR":0.9996}</code></strong></dt> |
| <dd>由正常优先级调用程序分发的所有事务的平均 (<code>avg</code>)、最差 (<code>wst</code>) 和最佳 (<code>bst</code>) 情况。两个事务 <code>miss</code> 截止时间,使得达标率为 (<code>meetR</code>) 0.9996。</dd> |
| |
| <dt><strong><code>"fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, |
| "meetR":1}</code></strong></dt> |
| <dd>类似于 <code>other_ms</code>,但适用于由具有 <code>rt_fifo</code> 优先级的客户端分发的事务。<code>fifo_ms</code> 的结果很可能(但不需要)优于 <code>other_ms</code>,且 <code>avg</code> 和 <code>wst</code> 值较低,而 <code>meetR</code> 则较高(如果考虑后台中的负荷,其差异可能会更大)。</dd> |
| |
| </dl> |
| |
| <p class="note"><strong>注意</strong>:后台负荷可能会影响延迟测试中的吞吐量结果和 <code>other_ms</code> 元组。只要后台负荷的优先级低于 <code>fifo_ms</code>,就可能只有 <code>RT-fifo</code> 会显示类似的结果。</p> |
| |
| <h4 id="pair-values">指定对值</h4> |
| <p>每个客户端进程都会与其专用的服务器进程配对,且每一对都可能会独立调度到任何 CPU。不过,只要同步标记是 <code>honor</code>,事务期间应该就不会出现 CPU 迁移的情况。</p> |
| |
| <p>确保系统没有过载!虽然过载系统中延迟较高是正常现象,但是针对过载系统的测试结果并不能提供有用的信息。要测试压力较高的系统,请使用 <code>-pair |
| #cpu-1</code>(或谨慎使用 <code>-pair #cpu</code>)。使用 <code>-pair <em>n</em></code> 和 <code><em>n</em> > #cpu</code> 进行测试会使系统过载,并生成无用信息。</p> |
| |
| <h4 id="deadline-values">指定截止时间值</h4> |
| <p>经过大量用户场景测试(在合格产品上运行延迟测试),我们决定将 2.5 毫秒定为需要满足的截止时间要求。对于具有更高要求的新应用(如每秒 1000 张照片),此截止时间值将发生变化。</p> |
| |
| <h4 id="verbose">指定详细输出</h4> |
| <p>使用 <code>-v</code> 选项显示详细输出。示例:</p> |
| |
| <pre class="devsite-click-to-copy"> |
| <code class="devsite-terminal">libhwbinder_latency -i 1 -v</code> |
| |
| <div style="color: orange">-------------------------------------------------- |
| service pid: 8674 tid: 8674 cpu: 1 |
| SCHED_OTHER 0</div> |
| -------------------------------------------------- |
| main pid: 8673 tid: 8673 cpu: 1 |
| |
| -------------------------------------------------- |
| client pid: 8677 tid: 8677 cpu: 0 |
| SCHED_OTHER 0 |
| |
| <div style="color: blue">-------------------------------------------------- |
| fifo-caller pid: 8677 tid: 8678 cpu: 0 |
| SCHED_FIFO 99 |
| |
| -------------------------------------------------- |
| hwbinder pid: 8674 tid: 8676 cpu: 0 |
| ??? 99</div> |
| <div style="color: green">-------------------------------------------------- |
| other-caller pid: 8677 tid: 8677 cpu: 0 |
| SCHED_OTHER 0 |
| |
| -------------------------------------------------- |
| hwbinder pid: 8674 tid: 8676 cpu: 0 |
| SCHED_OTHER 0</div> |
| </pre> |
| |
| <ul> |
| <li><font style="color:orange">服务线程</font>使用 <code>SCHED_OTHER</code> 优先级创建,且与 <code>pid |
| 8674</code> 要一起在 <code>CPU:1</code> 中运行。</li> |
| <li>随后,<font style="color:blue">第一个事务</font>由 <code>fifo-caller</code> 启动。为处理该事务,hwbinder 会将服务器 (<code>pid: 8674 tid: 8676</code>) 的优先级升级到 99,并使用瞬态调度类别(输出为 <code>???</code>)对其进行标记。接下来,调度程序会将服务器进程置于 <code>CPU:0</code> 中,以运行该进程并将它与其客户端使用的同一 CPU 进行同步。</li> |
| <li><font style="color:green">第二个事务</font>调用程序的优先级为 <code>SCHED_OTHER</code>。服务器自行降级并为优先级为 <code>SCHED_OTHER</code> 的调用程序提供服务。</li> |
| </ul> |
| |
| <h4 id="trace">使用跟踪记录进行调试</h4> |
| <p>您可以指定 <code>-trace</code> 选项来调试延迟问题。使用该选项时,延迟测试会在检测到不良延迟时停止跟踪日志记录。示例:</p> |
| |
| <pre class="prettyprint"> |
| <code class="devsite-terminal">atrace --async_start -b 8000 -c sched idle workq binder_driver sync freq</code> |
| <code class="devsite-terminal">libhwbinder_latency -deadline_us 50000 -trace -i 50000 -pair 3</code> |
| deadline triggered: halt &mp; stop trace |
| log:/sys/kernel/debug/tracing/trace |
| </pre> |
| |
| <p>以下组件可能会影响延迟:</p> |
| |
| <ul> |
| <li><strong>Android 编译模式</strong>。Eng 模式通常比 Userdebug 模式速度慢。</li> |
| <li><strong>框架</strong>。框架服务如何使用 <code>ioctl</code> 来配置 binder?</li> |
| <li><strong>binder 驱动程序</strong>。驱动程序是否支持精细锁定?驱动程序是否包含所有性能调整补丁程序? |
| </li><li><strong>内核版本</strong>。内核的实时性能越好,结果就越好。</li> |
| <li><strong>内核配置</strong>。内核配置是否包含 <code>DEBUG_PREEMPT</code> 和 <code>DEBUG_SPIN_LOCK</code> 等 <code>DEBUG</code> 配置?</li> |
| <li><strong>内核调度程序</strong>。内核中是否具有 Energy-Aware 调度程序 (EAS) 或异构多处理 (HMP) 调度程序?有没有内核驱动程序(<code>cpu-freq</code> 驱动程序、<code>cpu-idle</code> 驱动程序、<code>cpu-hotplug</code> 等)影响调度程序?</li> |
| </ul> |
| |
| </body></html> |