On Games’ Power Consumption and phones


I was watching the end of this wonderful GDC 2018 Vulkan on Android presentation but at the end of it a member of audience asked about power consumption, and I didn’t find the answers given to be useful enough.

While I haven’t worked on Vulkan particularly, I do have experience with Metal on iOS. Particularly because we had complaints from users that our app was (is) consuming too much battery.

Here’s the few things I can answer from my experience:

Generally speaking, a game that runs faster takes less time to issue commands and render. If VSync is on (which is forced on in Mobile) this means the chips will spend more time in idle state, and thus save battery.

Additionally, APIs like Vulkan allow you to better utilize the HW (e.g. avoid loading from RAM or clearing when binding a render target; share the same pointer between CPU & GPU without any copies; avoiding MTLStoreActionStore when possible, using framebuffer fetch instead of ping pong for postprocessing) which translates to lower power consumption.

Bandwidth is expensive in terms of power consumption, so the less texture fetches you use, the better. Compressed textures, low bpp formats, and low res textures fit easier in the cache, thus saving bandwidth and therefore, battery.

Overall, the longer your CPU & GPU stay in idle for continuous periods of time, the more you can save in battery.

But there are TWO GOTCHAS:

1. Let’s suppose you were hitting 45 fps with the CPU being the main bottleneck, and the GPU quite idle. Now with Vulkan/Metal you are able to significantly reduce CPU usage and reach 60 fps; but now your GPU is much more utilized in order to reach that target. Overall, you end up using more battery because both CPU & GPU are more busy when combined. You would have to artificially clamp the target framerate back to 45 fps in order to reduce power consumption.

This is quite common for us with the oldest models of iPads & iPad Minis featuring 4K retina displays: the GPU spends considerable time upscaling (or rendering natively in 4k) becoming a huge bottleneck, to the point the CPU spends most of the time sleeping and thus saving a lot of battery (but it’s impossible to hit 60 fps on these devices, at least with the level of minimum graphical fidelity we want our users to experience).

2. This is MUCH more important than anything else I said in this post so far: THE CPU & GPU GOVERNORS.

If your code is taking too much time, the kernel heuristics decide the app must be slow and unresponsive, so it increases the core frequency. The problem is… your app may just be fast enough to still be hitting 60 fps at low frequency!!!

You don’t care your game’s CPU usage (or GPU) takes 9ms per frame at low power and 2ms at high power. But the kernel doesn’t know it’s ok. It thinks your app may appear unresponsive.

So far we have encountered this is the major problem on power consumption. Our game perfectly hitting the intended target framerate, but the kernel increasing the frequency nonetheless. Unfortunately, AFAIK, there is no way to tell iOS or Android “keep me in low power mode at all times”.

All you know is that if your app consumes a bit fewer cycles and suddenly it’s 10x less power consumption. Get slightly careless and suddenly you’re **literally** consuming 2% per minute.
It’s an on/off thing, and no individual feature we’ve measured so far causes a visible change in power consumption, but switching all of them off at the same time makes the kernel do its thing and go into low power state. Let’s suppose you have features A, B & C. The only combination that saves battery is turning A, B & C off. But leave just one of them enabled and you end up consuming pretty much the same amount as having all of them on!

There’s also no published known reliable way to ask for the current CPU frequency on iOS programmatically, so you have to be doing lots of guesswork, such as watching power consumption on XCode instruments, CPU usage, externally comparing heat produced (when doing enough work that battery consumption begins dropping significantly faster vs when doing slightly less work but battery consumption drops slightly).

Overall it boils down to: If you care about power consumption, monitor it. If it’s unacceptable: optimize, replace Bandwidth by ALU, or do less. And sleep more.
If you still can’t fool the governors to lower the frequency, add a low power mode option, which lowers quality and locks the framerate to a lower target that fools the kernel (e.g. running at 30hz = more time sleeping). That’s what we did. It’s not perfect but it will make your users happier.

Remember that changes to your code or updates to your compiler may result in code that is optimized differently (or altering code or render order causes fewer/more cache misses) which is problematic if your game is walking right within the threshold of low and high power states. Also updates to iOS can change how the governor behaves.

You need to constantly monitor this.

If someone knows the private calls to get current CPU frequency (or to keep the governor in a specific power state), they’re welcomed! Even, if the private calls are not allowed for publishing in the App Store, they’re still very useful for debugging.

Bonus:
After I wrote this page (but before I published), INSIDE published very interesting slides (PDF version) on shipping INSIDE for iOS, which covers battery, frequency scaling, thermal throttling, and display brightness issues.