デバイスのパフォーマンスを評価するには、Simpleperf を使用します。Simpleperf は、Android のアプリとネイティブ プロセスの両方に対応するネイティブ プロファイリング ツールです。アプリの CPU 使用率とスレッド アクティビティをリアルタイムで検査するには、CPU Profiler を使用します。
次の 2 つのパフォーマンス インジケーターがユーザーに表示されます。
- 予測可能で知覚可能なパフォーマンス。ユーザー インターフェース(UI)がフレームをドロップするか、または 60 FPS で一貫してレンダリングするか。オーディオがアーティファクトまたはポップなしで再生されるか。ユーザーが画面をタップしてから画面にエフェクトが表示されるまでの遅延時間はどれくらいか。
- 長いオペレーションに必要な時間(アプリの起動など)。
前者の方が後者より目立ちます。通常、ユーザーはジャンクには気付きますが、2 台のデバイスを並べて見ない限り、アプリ起動時間が 500 ミリ秒か 600 ミリ秒かは区別できません。タップの遅延にはすぐに気付き、デバイスに対する認識に大きく影響します。
そのため高速なデバイスでは、UI パイプラインの機能を維持するために必要なことを除くと、UI パイプラインがシステムで最も重要です。つまり UI パイプラインでは、滑らかな UI に必要でない他のすべての作業をプリエンプトする必要があります。滑らかな UI を維持するには、UI 作業を実行できる場合は、バックグラウンド同期、通知配信などの作業をすべて遅延させる必要があります。滑らかな UI を維持するために、長いオペレーション(HDR+ ランタイム、アプリの起動など)のパフォーマンスを引き換えにすることは容認されます。
キャパシティとジッター
デバイスのパフォーマンスを考えるとき、キャパシティとジッターの 2 つは重要な指標です。
キャパシティ
キャパシティとは、デバイスが一定の時間にわたって占有するリソースの総量です。CPU リソース、GPU リソース、I/O リソース、ネットワーク リソース、メモリ帯域幅やその他類似の指標がこれに当てはまります。システム全体のパフォーマンスを調べるときは、個々のコンポーネントを抽象化し、パフォーマンスを決定する指標を 1 つ想定すると便利です(特に新しいデバイスをチューニングする場合、そのデバイスで実行されるワークロードが固定されている可能性が高いため)。
システムのキャパシティは、オンラインのコンピューティング リソースによって異なります。CPU / GPU の周波数の変更はキャパシティを変更する主な手段ですが、オンラインの CPU コア数を変更するなどの方法もあります。したがって、システムのキャパシティは消費電力と対応関係にあります。キャパシティを変更すると、消費電力も常に同じように変化します。
ある時点で必要なキャパシティは、実行中のアプリによって大きく左右されます。そのため、プラットフォームは所定のワークロードに必要なキャパシティを調整することがほとんどできず、そのための手段はランタイムの改善に限られます(Android フレームワーク、ART、Bionic、GPU コンパイラ / ドライバ、カーネル)。
ジッター
ワークロードに必要なキャパシティは簡単にわかりますが、ジッターの概念は漠然としています。高速なシステムを実現する障害となるジッターについては、『THE CASE OF THE MISSING SUPERCOMPUTER PERFORMANCE: ACHIEVING OPTIMAL PERFORMANCE ON THE 8,192 PROCESSORS OF ASCl Q』をご覧ください(これは ASCI Q スーパーコンピュータが期待されるパフォーマンスを達成できなかった理由についての調査であり、大規模システムを最適化するための導入として優れています)。
このページでは、ASCI Q の論文で「noise」と呼ばれているものを「ジッター」という用語で表しています。ジッターとは、ユーザーが知覚できる処理の実行を妨げるランダムなシステム動作です。多くの場合、実行しなければならない処理ですが、特定の時間に実行させるような厳密なタイミング要件はありません。発生するタイミングがランダムであるため、特定のワークロードについてジッターの存在を反証することは非常に困難です。また、既知のジッターの原因が特定のパフォーマンス問題の原因であると証明することも、非常に困難です。ジッターの原因の診断によく使用されるツール(トレースやロギングなど)自体がジッター発生の原因となることもあります。
実際の Android 実装で発生したジッターの原因には次のようなものがあります。
- スケジューラの遅延
- 割り込みハンドラ
- プリエンプションまたは割り込みが無効になっている状態でドライバコードが長時間実行されている
- 長時間実行 softirq
- ロック競合(アプリ、フレームワーク、カーネル ドライバ、バインダー ロック、mmap ロック)
- 優先度の低いスレッドがファイルのロックを保持し、優先度の高いスレッドの実行を妨げる、ファイル記述子の競合
- 遅延する可能性がある場合のワークキューでの UI クリティカルなコードの実行
- CPU のアイドルへの遷移
- ロギング
- I/O 遅延
- 不要なプロセスの作成(例:
CONNECTIVITY_CHANGE
ブロードキャスト) - 空きメモリ不足によるページ キャッシュのスラッシング
一定期間におけるジッターの所要時間は、キャパシティの増加に伴って減少する場合もあれば、減少しない場合もあります。たとえば、ドライバが i2c バスを介した読み込みを待機している間、割り込みを無効のままにしておくと、CPU が 384 MHz であっても 2 GHz であっても、一定の時間がかかります。キャパシティを増やすことは、ジッターが発生した場合にパフォーマンスを改善する適切なソリューションではありません。結果として、プロセッサを高速にすることでは、ジッター制限のある状況でのパフォーマンス改善はほとんどできません。
最終的に、キャパシティとは異なり、ジッターはほぼ完全にシステム ベンダーの領域内にあります。
メモリ消費
従来、メモリ消費はパフォーマンス低下の原因とされてきました。消費自体はパフォーマンスの問題ではありませんが、lowmemorykiller のオーバーヘッド、サービスの再起動、ページ キャッシュのスラッシングによってジッターが発生する可能性があります。メモリ消費を減らすことで、パフォーマンス低下の直接的な原因は回避できます。しかし、このような原因を回避する、より的を絞った改善策が他にも存在する可能性があります(たとえばフレームワークを固定して、直後にページインされるときにページアウトされないようにする)。
デバイスの初期パフォーマンスの分析
機能するもののパフォーマンスの低い状態から、ユーザーに見えるパフォーマンス低下の個々のケースを確認することでシステムの動作を修正しようとすることは、適切な戦略ではありません。通常、パフォーマンスの低下は再現が困難であるか(ジッターなど)、アプリの問題であり、システム全体で変数が多すぎるためです。結果として原因を誤認し、システム全体のパフォーマンスを大局的に改善する機会を逃す一方で、細部の改善に終始することになりがちです。
代わりに、新しいデバイスの開発に際し、次の一般的なアプローチを使用します。
- すべてのドライバを実行し、基本的な周波数ガバナー設定で、UI のシステム起動を行います(周波数ガバナーの設定を変更する場合は、下記のステップをすべて繰り返します)。
sched_blocked_reason
トレースポイントと、フレームがディスプレイに配信されるタイミングを示すディスプレイ パイプライン内の他のトレースポイントを、カーネルがサポートしていることを確認します。- 軽量で一定したワークロード(例: UiBench または TouchLatency のボールテスト)を実行しながら、UI パイプライン全体(IRQ を介した入力の受信から最終スキャンアウトまで)の長いトレースを行います。
- 軽量で一定したワークロードで検出されたフレーム落ちを修正します。
- フレーム落ちがない状態で一度に 20 秒以上実行できるまで、ステップ 3~4 を繰り返します。
- ユーザーに見える他のジャンクソースに移動します。
デバイス開発の初期段階で簡単にできることには、他に次のようなものがあります。
- カーネルに sched_blocked_reason トレースポイント パッチが適用されていることを確認します。このトレースポイントは、systrace の sched トレース カテゴリで有効になり、スレッドが割り込み不可スリープに入ったときにその原因となった関数を特定します。割り込み不可スリープはジッターの指標として非常に一般的なものであり、パフォーマンス分析で重要です。
- GPU とディスプレイ パイプラインのトレースが十分であることを確認します。比較的新しい Qualcomm SOC では、次の方法でトレースポイントを有効にします。
adb shell "echo 1 > /d/tracing/events/kgsl/enable"
adb shell "echo 1 > /d/tracing/events/mdss/enable"
これらのイベントは、systrace を実行しても有効なままであるため、mdss_fb0
セクションのディスプレイ パイプライン(MDSS)に関する追加情報をトレースで確認できます。Qualcomm SOC では、GPU に関する追加情報は標準の systrace ビューには表示されませんが、結果はトレース自体に含まれています(詳細については systrace についてをご覧ください)。
この種のディスプレイ トレースで必要なものは、フレームがディスプレイに配信されたことを直接示す単一のイベントです。そこから、フレーム時間に達したかどうかを判断できます。イベント Xn がイベント Xn-1 の後 16.7 ミリ秒未満で発生した場合(60 Hz ディスプレイを想定)、ジャンクが発生していないことがわかります。SOC がそのようなシグナルを提供していない場合は、ベンダーと協力して入手してください。ジッターのデバッグは、フレーム完了の決定的なシグナルなしでは非常に困難です。
合成ベンチマークの使用
合成ベンチマークは、デバイスの基本機能が存在することを確認するのに役立ちます。しかし、ユーザーに認識されるデバイス パフォーマンスの代用としてベンチマークを扱うことは有用ではありません。
SOC での経験によれば、SOC 間の合成ベンチマーク パフォーマンスの違いは、認識可能な UI パフォーマンスでの同様の違い(フレーム落ちの数、99 パーセンタイル フレーム時間など)とは関係ありません。合成ベンチマークはキャパシティ専用のベンチマークです。ジッターがベンチマークの測定パフォーマンスに与える影響は、ベンチマークの全体的な実行時間に対するもののみです。結果として合成ベンチマーク スコアは、ユーザーが認識するパフォーマンスの指標としては、ほとんど意味がありません。
2 つの SOC が、1,000 フレームの UI をレンダリングし合計レンダリング時間をレポートするベンチマーク X を実行しているとします(スコアが小さい方がよい)。
- SOC 1 はベンチマーク X の各フレームを 10 ミリ秒でレンダリングし、スコア 10,000 です。
- SOC 2 はフレームの 99% を 1 ミリ秒でレンダリングしますが、フレームの 1% を 100 ミリ秒でレンダリングし、スコア 19,900 です。スコアが大幅に優れています。
ベンチマークが実際の UI パフォーマンスを示す場合、SOC 2 は使用できません。リフレッシュ レートを 60 Hz と仮定すると、SOC 2 では 1.5 秒ごとにジャンク フレームが発生します。一方、SOC 1(ベンチマーク X では遅いとされる SOC)は完全に滑らかに動作します。
バグレポートの使用
バグレポートはパフォーマンス分析に役立つことがありますが、非常に重いため、散発的なジャンクの問題のデバッグにはほとんど役に立ちません。特定の時点でシステムが何をしていたかについてのヒントが得られることはあります。特にアプリの遷移でジャンクがあった場合(バグレポートにログが記録されます)、役に立ちます。バグレポートは、システムの実効キャパシティを減少させる可能性がある、より広範なシステムの問題(サーマル スロットリングやメモリの断片化など)が発生していることを表すこともあります。
TouchLatency の使用
Google Pixel と Goolge Pixel XL で使用される推奨の周期的ワークロードである TouchLatency によって、動作不良の例をいくつか得られます。TouchLatency は frameworks/base/tests/TouchLatency
から入手でき、タップの遅延と弾むボールという 2 つのモードがあります(モードを切り替えるには、右上のボタンをクリックします)。
弾むボールのテストは、ユーザーの入力にかかわらず画面中で永遠にボールが弾むという単純なものです。通常は完璧に動作させるのが飛び抜けて難しいテストでもありますが、フレーム落ちがない状態に近いほど、デバイスは優れています。弾むボールのテストは非常に低いクロックで実行される、些細ではあるものの完全に一貫したワークロードであるため、困難です(これは、デバイスに周波数ガバナーがあることを前提としています。代わりに、デバイスが固定クロックで実行されている場合、弾むボールのテストを初めて実行するとき、CPU / GPU をほぼ最小までダウンクロックします)。システムが休止し、クロックがアイドルに近づくと、1 フレームあたりの必要な CPU / GPU 時間が増加します。ボールを見ることでジャンクの様子を確認できます。フレーム落ちも systrace で確認できます。
ユーザーに見えるワークロードのほとんどと比較した場合、ワークロードが非常に一定しているため、各フレーム落ちの間にシステムで実行されている UI パイプライン以外のものを正確に追跡することで、ジッターを引き起こす原因の大部分の特定をより簡単に実現できます。クロックが低いと、ジッターがフレーム落ちを引き起こす可能性が高まることで、ジッターの影響が増します。そのため、TouchLatency が 60 FPS に近いほど、大規模なアプリで散発的かつ再現困難なジャンクを引き起こすようなシステムの動作不良が発生する可能性は低くなります。
ジッターはクロックスピードに左右されないことが多いため(必ずしもそうとは限りません)、ジッターの診断には非常に低いクロックで実行されるテストを使用します。理由は次のとおりです。
- すべてのジッターがクロックスピードに左右されないわけではありません。発生源の多くは CPU 時間を消費します。
- ガバナーは、クロックダウンによって平均フレーム時間をデッドラインに近づけるため、UI 以外の処理の実行に時間を消費することによってフレーム落ちの限界を超えるようになります。