This guide describes how to use the Android Dynamic Performance Framework (ADPF) to optimize games based on the dynamic thermal, CPU, and GPU management features on Android. The focus is on games, but you can also use the features for other performance-intensive apps.
ADPF is a set of APIs that allow games and performance-intensive apps to interact more directly with power and thermal systems of Android devices. With these APIs, you can monitor the dynamic behavior on Android systems and optimize game performance at a sustainable level that doesn't overheat devices.
Mobile SoCs and Android have more dynamic performance behaviors than desktops and consoles. These behaviors include thermal state management, varying CPU and GPU clocks, and varying CPU core types. This combined with the increasingly diverse core topology of SoCs creates challenges when trying to ensure that your game can leverage this behavior without negatively impacting device performance. ADPF provides some of this information in order to make performance more predictable.
Here are the main ADPF features:
Thermal-state monitoring: Monitor the thermal state of a device and then proactively adjust performance before it becomes unsustainable.
CPU performance hints: Provide performance hints that let Android choose the right CPU clocks and core types instead of Android choosing based on previous workloads.
Fixed-performance mode: Enable fixed-performance mode on a device during benchmarking to get measurements that aren't altered by dynamic CPU clocking.
Released: Android 11 (API level 30)
The potential performance of your app is limited by the thermal state of the device, which can vary based on characteristics such as weather, recent usage, and the device's thermal design. Devices can only maintain high levels of performance for a limited amount of time before being thermally throttled. A key goal of your implementation should be to achieve performance goals without exceeding thermal limitations. Furthermore, when debugging performance issues, knowing when the thermal state of a device limits performance is important.
Game engines usually have runtime performance parameters that can adjust the workload the engine puts on the device. For example, these parameters can set the number of worker threads, worker-thread affinity for big and small cores, GPU fidelity options, and framebuffer resolutions.
When a device approaches an unsafe thermal state, your game can avoid being throttled by decreasing the workload through these parameters. To avoid throttling, you should monitor the thermal state of the device and proactively adjust the game engine workload. Once the device overheats, the workload must drop below the sustainable performance level in order to dissipate heat.
ADPF provides the
PowerManager class for monitoring the
thermal state of a device. Here are the main elements:
If the device is not under thermal throttling:
Some throttling, but doesn't have a big impact on performance:
Significant throttling that impacts performance:
Native binding for the thermal API (NDK):
You can monitor the thermal state of the device by polling the
method. This method determines how long the device can maintain the current
performance level without overheating. If the time is less than the amount
needed to run the workload, then your game should decrease the workload to a
sustainable level. For example, the game can shift to smaller cores, reduce the
frame rate, or lower fidelity.
CPU performance hints
Released: Android 12 (API level 31)
With CPU performance hints, a game can influence dynamic CPU performance behavior without overheating the device and wasting power. 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.
When Android devices dynamically adjust CPU clock speed, the frequency can change the performance impact 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 can temporarily reduce jank and increase responsiveness by running your game at peak clock speeds, but it drains power and eventually leads to thermal throttling of the clocks. When CPU or GPU clocks are throttled, they perform below the sustainable level.
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 wastes power and increases the thermal load on the device when the app isn't using the additional resources.
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.
ADPF provides the
class so games can make 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:
Create hint sessions for key threads that behave similarly. For example:
- Rendering threads get one session
- IO threads get another session
- Audio threads get a third session
The game should do this early, at least 2ms and preferably more than 4ms before a session needs increased system resources.
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.
Released: Android 11 (API level 30)
Android devices can change clocking dynamically based on the system load. This behavior is good for power savings during use, but can make it difficult to get reliable performance data. If you are trying to determine how fast a code fragment can run for regression prevention, or if an optimization is repeatable, your results won't be reliable if they aren't tested at fixed clock speeds. With fixed clocks, you can do accurate A/B testing of performance without changes in the CPU frequency being a factor.
Fixed-performance mode sets CPU and GPU clocks with an upper and lower bound. This mode does not disable other dynamic performance behaviors, such as core selection.
You can enable fixed-performance mode with the following
adb shell cmd power set-fixed-performance-mode-enabled [true|false]
A device that is running in fixed-performance mode can still overheat because the mode doesn't put the device into a thermally-sustainable state. Because of this, we recommend the following for benchmark runs:
Wait for the device to return to a thermally-sustainable state before starting the run.
Monitor the thermal state of the device during testing to differentiate the impact between the benchmark code and thermal events.
demonstrates the basic use of the ADPF API. The sample displays the device's
thermal status using the ADPF
API and the
API. The app also dynamically changes the workload based on the API's hint and
API to control render thread performance.