**Released**:

Android 12 (API Level 31) - [Performance Hint API](https://developer.android.com/reference/android/os/PerformanceHintManager)

Android 13 (API Level 33) - [Performance Hint Manager in the NDK API](https://developer.android.com/ndk/reference/group/a-performance-hint)

(Preview) Android 15 (DP1) - [`reportActualWorkDuration()`](https://developer.android.com/reference/android/os/PerformanceHintManager.Session#reportActualWorkDuration(android.os.WorkDuration))

With CPU performance hints, a game can influence dynamic CPU performance
behavior to better match its needs. On most devices, Android dynamically adjusts
the CPU clock speed and core type for a workload based on the previous demands.
If a workload uses more CPU resources, the clock speed is increased and the
workload is eventually moved to a larger core. If the workload uses less
resources, then Android lowers resource allocation. With ADPF, the application
or game can send an additional signal about its performance and deadlines. This
helps the system ramp up more aggressively (improving performance) and lower the
clocks quickly when the workload is complete (saving power usage).

## Clock speed

When Android devices dynamically adjust their CPU clock speed, the frequency can
change the performance of your code. Designing code that addresses dynamic clock
speeds is important for maximizing performance, maintaining a safe thermal
state, and using power efficiently. You cannot directly assign CPU frequencies
in your app code. As a result, a common way for apps to attempt to run at higher
CPU clock speeds is to run a busy loop in a background thread so the workload
seems more demanding. This is a bad practice as it wastes power and increases
the thermal load on the device when the app isn't actually using the additional
resources. The CPU `PerformanceHint` API is designed to address this problem. By
letting the system know the actual work duration and the target work duration,
Android will be able to get an overview of the app's CPU needs and allocate
resources efficiently. This will lead to optimum performance at efficient power
consumption level.

## Core types

The CPU core types that your game runs on are another important performance
factor. Android devices often change the CPU core assigned to a thread
dynamically based on recent workload behavior. CPU core assignment is even more
complex on SoCs with multiple core types. On some of these devices, the larger
cores can only be used briefly without going into a thermally unsustainable
state.

Your game shouldn't try to set the CPU core affinity for the following reasons:

- The best core type for a workload varies by device model.
- The sustainability of running larger cores varies by SoC and by the various thermal solutions provided by each device model.
- The environmental impact on the thermal state can further complicate core choice. For example, the weather or a phone case can change the thermal state of a device.
- Core selection can't accommodate new devices with additional performance and thermal capabilities. As a result, devices often ignore a game's processor affinity.

### Example of default Linux scheduler behavior

![Linux Scheduler Behavior](https://developer.android.com/static/games/optimize/adpf/images/adpf_linux_sched_behavior.png) **Figure 1.** Governor can take \~200ms to ramp up or down CPU frequency. ADPF works with the Dynamic Voltage and Frequency Scaling system (DVFS) to provide best performance per watt

### PerformanceHint API abstracts more than DVFS latencies

![ADPF Abstracts more than DVFS Latencies](https://developer.android.com/static/games/optimize/adpf/images/adpf_dvfs_latency.png) **Figure 2.** ADPF knows how to make the best decision on your behalf

- If the tasks need to run on a specific CPU, PerformanceHint API knows how to make that decision on your behalf.
- Therefore, you need not use affinity.
- Devices come with various topologies; Power and thermal characteristics are too varied to be exposed to app developer.
- You can't make any assumptions about the underlying system you're running on.

## Solution

ADPF provides the [`PerformanceHintManager`](https://developer.android.com/reference/android/os/PerformanceHintManager)
class so games can send performance hints to Android for CPU clock speed and
core type. The OS can then decide how to best use the hints based on the SoC and
thermal solution of the device. If your app uses this API along with thermal
state monitoring, it can provide more informed hints to the OS instead of using
busy loops and other coding techniques that can cause throttling.

This is how a game uses performance hints:

1. [Create hint sessions](https://developer.android.com/reference/android/os/PerformanceHintManager#createHintSession(int%5B%5D,%20long)) for key threads that behave similarly. For example:
   - Rendering thread and its dependencies get one session
     1. In Cocos, the main engine thread and render thread gets [one
        session](https://github.com/cocos/cocos-engine/blob/v3.8.5-dianchu/native/cocos/base/threading/MessageQueue.cpp)
     2. In Unity, integrate [Adaptive Performance Android Provider plugin](https://docs.unity3d.com/Packages/com.unity.adaptiveperformance.google.android@1.2/manual/index.html)
     3. In Unreal, integrate Unreal Adaptive Performance plugin and use [Scalability options](https://docs.unrealengine.com/4.27/en-US/TestingAndOptimization/PerformanceAndProfiling/Scalability/ScalabilityReference/) to support multiple quality levels
   - IO threads get another session
   - Audio threads get a third session
2. The game should do this early, at least 2ms and preferably more than 4ms before a session needs increased system resources.
3. In each hint session, predict the duration needed for each session to run. The typical duration is equivalent to a frame interval, but the app can use a shorter interval if the workload does not vary significantly across frames.

Here is how to put the theory into practice:

### Initialize PerformanceHintManager and createHintSession

Get the manager using system service and create a hint session for your thread
or thread group working on the same workload.  

### 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);

### Set threads if necessary

**Released**:

Android 11 (API Level 34)

Use the [`setThreads`](https://developer.android.com/reference/android/os/PerformanceHintManager.Session#setThreads(int%5B%5D))
function of the `PerformanceHintManager.Session` when you have other threads
that need to be added later. For example, if you create your physics thread
later and need to add it to the session, you can use this `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);

If you are targeting lower API Levels, you will need to destroy the session and
re-create a new session every time you need to change the thread IDs.

### Report Actual Work Duration

Track the actual duration needed to complete the work in nanoseconds and report
it to the system upon completion of the work on every cycle. For example, if
this is for your rendering threads, call this on every frame.

To get the actual time reliably, use:  

### 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();

For example:  

### 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);

### Update Target Work Duration when necessary

Whenever your target work duration changes, for example if the player chooses a
different target fps, call the [`updateTargetWorkDuration`](https://developer.android.com/reference/android/os/PerformanceHintManager.Session#updateTargetWorkDuration(long))
method to let the system know so that the OS can adjust the resources according
to the new target. You don't have to call it on every frame and only need to
call it when the target duration changes.  

### C++

    APerformanceHint_updateTargetWorkDuration(hint_session, target_duration);

### Java

    hintSession.updateTargetWorkDuration(targetDuration);