Performance Hint API

リリース日:

Android 12(API レベル 31)- Performance Hint API

Android 13(API レベル 33) - NDK API のパフォーマンス ヒント マネージャー

(プレビュー)Android 15(DP1)- reportActualWorkDuration()

CPU パフォーマンスのヒントにより、ゲームが動的な CPU パフォーマンスに影響を与える可能性がある より的確に判断できるようになりますほとんどのデバイスでは、Android は 以前の需要に基づくワークロードの CPU クロック速度とコアタイプ。 ワークロードで使用される CPU リソースが増えると、クロック速度が上がり、 ワークロードが最終的に大規模なコアに移動される。ワークロードの使用量が リソースの割り当てを減らします。ADPF では パフォーマンスや期限に関する追加のシグナルを送信できるこの システムが積極的に稼働し(パフォーマンスが向上)、 迅速にクロック クロックする(消費電力を節約する)。

クロック速度

Android デバイスが CPU クロック速度を動的に調整する場合、 コードのパフォーマンスを変更できます。動的クロックに対応するコードの設計 速度は、パフォーマンスの最大化、安全な温度の維持のために重要です。 効率的に電力を消費できます。CPU 周波数を直接割り当てることはできない 必要があります。そのため、アプリがより高い温度で実行しようとする一般的な方法は、 CPU クロック速度は、バックグラウンド スレッドでビジーループを実行するため、 要求が厳しいようです。この方法は電力を無駄に消費し、 デバイスの熱負荷(アプリが追加の 説明します。CPU PerformanceHint API は、この問題に対処するように設計されています。方法 実際の処理時間と目標処理時間をシステムに知らせる Android は、アプリの CPU ニーズの概要を取得して、CPU 使用率を 効率的に行うことができます。これにより、効率的な電力消費で最適なパフォーマンスが得られます。 できます。

コアの種類

ゲームを実行する CPU コアの種類も、もう 1 つの重要なパフォーマンス要因です。Android デバイスは、一般に、スレッドに割り当てられた CPU コアを最近のワークロード動作に基づいて動的に変更します。コアの種類が複数ある SoC では、CPU コアの割り当てはさらに複雑になります。これらのデバイスの一部では、大きなコアは短時間しか使用できず、温度的に持続できない状態になることはありません。

ゲームでは、次の理由により CPU コア アフィニティを設定しないようにしてください。

  • ワークロードに最適なコアの種類は、デバイスのモデルによって異なります。
  • 大きなコアの実行の持続可能性は、SoC によって異なります。また、各デバイスのモデルに用意されている各種の温度ソリューションによっても異なります。
  • 環境が温度状態に与える影響は、コアの選択をさらに複雑にする可能性があります。たとえば、天気やスマートフォン ケースにより、デバイスの温度状態が変わることがあります。
  • コアの選択は、パフォーマンス機能や温度機能が追加された新しいデバイスには適応しない可能性があります。このため、デバイスは一般に、ゲームのプロセッサ アフィニティを無視します。

Linux スケジューラのデフォルトの動作例

Linux スケジューラの動作
図 1. ガバナーによる CPU 周波数の増減には、最大 200 ミリ秒かかることがあります。ADPF は動的電圧および周波数スケーリング システム(DVFS)と連携して、1 ワットあたりのパフォーマンスを最大化します。

PerformanceHint API の抽象化は DVFS レイテンシを超える

ADPF の抽象化は DVFS レイテンシを超える
図 2. ADPF は、お客様に代わって最善の判断を行う方法を知っています
<ph type="x-smartling-placeholder">
    </ph>
  • タスクを特定の CPU で実行する必要がある場合は、PerformanceHint API に お客様に代わってその意思決定を行えます。
  • したがって、アフィニティを使用する必要はありません。
  • デバイスにはさまざまなトポロジが備わっている。電力特性と熱特性は 多様すぎてアプリ デベロッパーには公開できません。
  • 実行している基盤となるシステムについてなんらかの仮定を行うことはできません。

解決策

ADPF には PerformanceHintManager が用意されています。 クラスにより、ゲームは CPU クロック速度と CPU クロック速度に関するパフォーマンスのヒントを Android に送信し、 あります。OS はデバイスの SoC と温度ソリューションに基づき、そのヒントを最適に使用する方法を判断できます。アプリがこの API と温度状態のモニタリングを使用すれば、スロットリングを招く可能性があるビジーループやその他のコーディング手法を使用する代わりに、OS にさらに多くの情報を含むヒントを提供できます。

ゲームがパフォーマンスのヒントを使用する仕組みは以下のとおりです。

  1. 同様に動作するキースレッドのヒント セッションを作成します。たとえば、次のようになります。
    • レンダリング スレッドとその依存関係が 1 つのセッションを取得する <ph type="x-smartling-placeholder">
        </ph>
      1. Cocos では、メイン エンジン スレッドとレンダリング スレッドが 1 つ セッション
      2. Unity で Adaptive Performance Android Provider プラグインを統合します。
      3. Unreal で、Unreal Adaptive Performance プラグインを統合し、 複数の品質レベルをサポートするスケーラビリティ オプション
    • IO スレッドが別のセッションを取得する
    • オーディオ スレッドが 3 番目のセッションを取得する
  2. ゲームは、セッションがさらにシステム リソースを必要とする少なくとも 2 ミリ秒前、可能であれば 4 ミリ秒より前の早い段階で次の処理を行う必要があります。
  3. ヒント セッションごとに、各セッションの実行に必要な時間を予測します。一般的な継続時間はフレーム間隔と同じですが、アプリでは 間隔が短くなります。

この理論を実践する方法は次のとおりです。

PerformanceHintManager と createHintSession を初期化する

システム サービスを使用してマネージャーを取得し、スレッドのヒント セッションを作成する 1 つのスレッドグループがあります

C++

int32_t tids[1];
tids[0] = gettid();
int64_t target_fps_nanos = getFpsNanos();
APerformanceHintManager* hint_manager = APerformanceHint_getManager();
APerformanceHintSession* hint_session =
  APerformanceHint_createSession(hint_manager, tids, 1, target_fps_nanos);

Java

int[] tids = {
  android.os.Process.myTid()
};
long targetFpsNanos = getFpsNanos();
PerformanceHintManager performanceHintManager =
  (PerformanceHintManager) this.getSystemService(Context.PERFORMANCE_HINT_SERVICE);
PerformanceHintManager.Session hintSession =
  performanceHintManager.createHintSession(tids, targetFpsNanos);

必要に応じてスレッドを設定する

リリース日:

Android 11(API レベル 34)

setThreads を使用する 他のスレッドがある場合の PerformanceHintManager.Session の関数 後で追加する必要がありますたとえば、物理スレッドを作成し、 後でセッションに追加する必要がある場合は、この setThreads API を使用できます。

C++

auto tids = thread_ids.data();
std::size_t size = thread_ids_.size();
APerformanceHint_setThreads(hint_session, tids, size);

Java

int[] tids = new int[3];

// add all your thread IDs. Remember to use android.os.Process.myTid() as that
// is the linux native thread-id.
// Thread.currentThread().getId() will not work because it is jvm's thread-id.
hintSession.setThreads(tids);

それより低い API レベルを対象としている場合は、セッションを破棄し、 スレッド ID の変更が必要になるたびに、新しいセッションを再作成する必要があります。

実際の作業時間を報告

作業の完了に必要な実際の時間をナノ秒単位で追跡し、レポートを生成する 各サイクルで作業が完了したら、システムに提供します。たとえば レンダリング スレッド用のものなので、フレームごとに呼び出します。

実際の時刻を確実に取得するには、次のコマンドを使用します。

C++

clock_gettime(CLOCK_MONOTONIC, &clock); // if you prefer "C" way from <time.h>
// or
std::chrono::high_resolution_clock::now(); // if you prefer "C++" way from <chrono>

Java

System.nanoTime();

例:

C++

// All timings should be from `std::chrono::steady_clock` or `clock_gettime(CLOCK_MONOTONIC, ...)`
auto start_time = std::chrono::high_resolution_clock::now();

// do work

auto end_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count();
int64_t actual_duration = static_cast<int64_t>(duration);

APerformanceHint_reportActualWorkDuration(hint_session, actual_duration);

Java

long startTime = System.nanoTime();

// do work

long endTime = System.nanoTime();
long duration = endTime - startTime;

hintSession.reportActualWorkDuration(duration);

必要に応じて目標作業時間を更新する

目標作業時間が変更された場合(たとえば、プレーヤーがゲームを選択した場合など) 目標 FPS が異なる場合は、updateTargetWorkDuration を呼び出します。 メソッドを使用してシステムに通知します。これにより、OS は 新しいターゲットに追加します。すべてのフレームで呼び出す必要はなく、 ターゲット期間が変更されたときに呼び出されます。

C++

APerformanceHint_updateTargetWorkDuration(hint_session, target_duration);

Java

hintSession.updateTargetWorkDuration(targetDuration);