Prevent CPU frequency scaling when profiling for iOS?

Hi all,


I've been digging into Instruments lately to investigate some potential performance wins for my app. I'm really enjoying the level of detail in the System Trace template, and the os_signpost stuff is perfect to get a high-level view of where to focus attention - great job!


I'm using the latest Xcode Version 11.4.1 (11E503a) on Catalina, and an iPod Touch 7th Gen on the latest iOS - 13.4 (17E255).


I'm measuring the app in a steady state where the workload is pretty constant and low (should be no issue hitting 60FPS rendering) but Xcode's CPU meter is pretty jumpy and it drops a frame every couple of seconds, which then seems to coincide with a reduction of CPU usage shown in Xcode.


My suspicion is the device thinks it can maintain the required framerate and use less energy by reducing the CPU frequency. This is not quite true for my app - the main thread is sleeping for a significant period but it is awaiting a result on the run loop before the frame rendering can be completed. I can see why the OS might think downclocking is possible here, but then kick up the frequency again when it sees a frame is dropped.


I did however once seem to get the device into a state where it was running at (I assume) full speed - the CPU meter in Xcode stayed steady as a rock at 15% or so (with "other processes" at a similar number). I haven't been able to figure out exactly how I managed that - Instruments was running a windowed tracing session and I app switched away and back to the app, but that method doesn't seem to work every time.


So the Instruments-related questions arising from this:

1) Is there a way to record and visualize the CPU frequency scaling in Instruments? If the frequency of the CPU is changing and unknown, then the timings coming out of Instruments are not really telling the whole story.

2) Is there an officially supported method to prevent CPU frequency scaling whilst Instruments is recording data?


Thanks for any help!


Simon

Post not yet marked as solved Up vote post of tangobravo Down vote post of tangobravo
2.3k views

Replies

Here's the Xcode CPU usage graph for my app, showing the difference between the "jumpy" usage and the low steady-state - the app work is the same throughout (essentially an idle animation loop). The times where it drops down to zero are when I double-tapped home to go into the app switcher (all the work done by the app is stopped on willResignActive), and then the jumps back up are tapping on the app to re-activate it.

NB: The image above shows up when I edit the post, but not for me in the main thread view - I've uploaded it here too: https://tango-bravo.net/ios-cpu-graph.png


You can see how much more stable the CPU usage is after the final (very short) trip to the switcher and back again. The overall pie chart suggested 150% or thereabouts of the CPU was "free" during this time.


My current process is to repeat this app switching until I obtain that state, and then record more detail in Instruments so that I can have more confidence the timings should be more consistent and comparable. Something more repeatable would be nice of course, and I suspect I have just stumbled onto some bug in the CPU frequency governor code with this "app switching" trick.


I'm updating to iOS 13.4.1 now to see if that changes anything.

Hi Simon,

Thanks for your feedback here.

First, answering to your questions:

There's currently no way of viewing Cores frequency in Instruments, nor lock the frequency of a device. If you could file a new request for this in Feedback Assistant — it'd be greatly appreciated. We try to prioritize depending on users needs, so it helps us track it.

We're aware that it's important for the users to have repeatable and stable environment for profiling. The only way of affecting your device condition from the tools are currently:
— Condition Inducers in Xcode that allow you to simulate certain device states,
— Locking number of CPUs in Instruments (Mac devices only)

As far as solving your problem:
It'd be interested to put signposts in the code you're measuring to see what's the standard deviation of duration of this operation and try to diagnose whether it's indeed because operation is taking more time.

It's also important to take a look at your thread with System Trace / Time Profiler and see whether it's ready to run or maybe being blocked by some other activity on the system.

If you're able to capture a trace of your problem — I'm happy to take a look and help you diagnose it.

Thanks,

Kacper




also would be interested to know and/or lock CPU frequency on iOS devices for profiling. it's difficult to measure and verify incremental performance improvements. my current strategy is to "measure" CPU frequency by running a fixed workload. i also try to prevent scaling by introducing an artificial workload, hoping to pin the CPU to the highest frequency. these two measures combined seem to give me something workable for now, but obviously it is not an ideal solution.