While Unity offers a wide range of tools for Profiling and Optimising your game, such as the Profiler window and Memory Profiler, there are cases when you need to go more in-depth and really understand what's happening in your device.
To do that on iOS and Mac, we can take advantage of Instruments, a performance analysis tool included as part of the Xcode toolset.
Let's take a look at what Instruments can offer, and how to set it up.
For this tutorial, I’m using Unity 6000.0.23, and XCode 16.2. Available features and settings might differ in other versions.
Preparing the build
In order to run your game build through XCode Instruments, we need to ensure the right options are selected when generating your build.
The first step is to make an iOS Build of your game from Unity.
Ensure Autoconnect Profiler, Deep Profiling Support, and Script Debugging are disabled. These options are not necessary for XCode Instruments and have a significant impact on performance, leading to capture results that are not representative of the actual game performance.
If your build fails with an “xcrun: error: SDK "iphoneos" cannot be located” error, this is likely due to a bug in the Xcode settings window where the CLI tools are not being detected. To fix that, open Xcode, go to Settings - Locations, and re-select Command Line Tools.
Now that we have a build, open the output project in XCode, and ensure a Provisioning Profile and Team are selected.
Then, connect your mobile device to your computer. You will need to have Developer Mode enabled on your device, you can do that by going to Settings - Privacy & Security - Developer Mode, then restarting it.
Before starting our profiling session, let’s make sure we have setup symbols.
Setting up Symbols
Symbols are files, containing class names, global variables, method and function names, along with extra information such as line numbers, that can allow you to effectively debug your running app. They are contained in a dSYM file.
After a successful build, you can retrieve symbols for your game by going to /Users/[username]/Library/Developer/Xcode/DerivedData/[AppFolder]/Build/Products/[AppProductFolder]/[AppName].app.dSYM
Your game client will have its own symbols file, then for every additional framework (including the UnityFramework) additional symbols will be generated and loaded.
In order to check and load symbols, open Instruments by going to XCode - Open Developer Tools - Instruments, then go to File - Symbols, and look for your game process. If Instruments already has a process attached, that should be applied automatically as a filter.
Here's how it looks like:
In this example, I have my game and four frameworks. You can see from the Status column if symbols are correctly loaded. In my experience, XCode will automatically load the symbols for your game and most of the frameworks, but if not, from that window you can add symbols by using the "+ Add Symbols" button and navigating to the folder mentioned previously.
Setting up Instruments
We now need to build and run our game in Xcode for profiling, by selecting Product - Build for - Profiling.
As a reminder:
- Run will just run your app, either on device or on the simulator.
- Profile will run the game with Instruments attached, which is what we need.
- Test will run unit tests.
Ensure the correct device is selected from the dropdown in the top bar.
Then, go to Product - Profile (or long click on the Run button and select Profile) to deploy the game on device with Instruments enabled.
When that succeeds, XCode will open the Instruments application and you'll be shown a window to select a Profiling template:
Alternatively, if you already have the game built and running on device, you can open Instruments by going to XCode - Open Developer Tools - Instruments, or directly through Spotlight search.
Templates and Traces
When you start profiling your application, Instruments will collect a bunch of data and metrics in what's called a Trace document. The type of data will depend on the Profiling template you chose.
A template is a collection of tracks that covers the areas you're interested in. For example if you choose the Network template, it will track HTTP Traffic, Network Connections and Point of Interest.
You are not limited to the tracks included in the template you picked, you can add more to your trace document by using the + button on the top right.
When you are happy with the selection of tracks you have, you can save it as a new Template, which will then show up in the initial window whenever you create a new trace document.
After setting up your trace document, you’ll be able to tweak settings for each individual track you added, and the Recorder itself. Specifically, you can select whether you want to visualise data as you capture it, or switch to Deferred mode where data is collected and then processed only when you terminate the session.
Deferred tends to give more accurate results, as Immediate can have an impact on the game performance, so it should be preferred whenever possible.
Depending on the tracks you selected, some Recording Mode won’t be available. For example, having System Call Trace in your document will force you to use Deferred, while the Time Profiler will prevent you from selecting the Capture last x seconds mode.
When you’re done tweaking your settings, you will need to attach to a running process by selecting it in the top right dropdown. Then, press the Record button to start capturing data.
Depending on the Recording Mode you selected, Instruments will start showing data in real-time, or it will record performance metrics until you have collected enough samples. In that case, you can stop it after recording a relevant gameplay chunk, and analyse your results.
You can save the trace for future reference, or to share with other team members, by going to File -> Save as...
Let's take a look at some of the most useful templates and tracks to analyse your game.
The Game Performance Template
This template is the best starting point for Game Development on iOS. It contains a collection of tracks showing info about your game performance, including CPU Usage, Thread states, GPU load, Thermal State, average framerate, Virtual Memory Traces, and metrics about your game Process.
You can select each individual track to retrieve more granular information about it. For example, here’s the Time Profiler.
The Time Profiler Track
The Time Profiler is a powerful sampling-based profiling tool. It collects data on threads and processes running on the CPUs, at regular intervals, that you can use to identify bottlenecks and inspect the related callstacks.
You can quickly inspect the stack traces taking the most time by looking at the pane on the right.
Since this is a sampling-based tool, the Time Profiler will tell you how much CPU time each thread spends on a method, but not how often that method was called or individual metrics about each call.
By default, the sampling frequency is 1ms.
You can separate profiling data by Thread by selecting the relevant option in the Call Tree menu at the bottom, as well as hiding System Libraries, flatten Recursion and other convenient view settings.
The Display Track
The Display track allows you to monitor the average game framerate, tracking each frame duration.
In this case, the game is running at a stable 60 FPS.
If your game is rendering to multiple displays, you’ll be able to see the target display for each frame in the first column.
Instruments will also spawn a sub-track for each connected display, showing frame duration and every VSync event at a glance.
The Game Memory Template
The Game Memory template is focused on memory usage profiling, showing Allocations, Virtual Memory Trace, GPU Memory usage, and other relevant metrics.
The Allocations track
This track will show all heap and virtual memory allocations, grouping them by category, and record reference-counting events.
If you press the small arrow next to each category, you can expand it and see exactly what allocations are being made.
In order to understand where allocations are coming from, you can switch to the Call Trees view, which will provide the callstack that's generating the allocation.
If you are tracking down Memory Leaks, you can tweak the Allocation Lifespan filter at the bottom of the window, selecting "Created & Persistent". This way, memory that has been freed by the end of the snapshot you selected won't be shown in the list.
The Activity Monitor
If you want to track device resources in real-time, you can use the Activity Monitor. It will track and collect information on the processes running on your device, including how much CPU and Memory they're consuming.
As shown in the screenshot, you can toggle the Graph Display to either show CPU, Memory, Disk or Network activity.
While this won't include detailed info about your game itself, it's useful to see how it compares to the other processes in the system.
The Thermal State Track
This track will monitor the Thermal state of your device, essentially how hot it gets. Possible values are Nominal, Fair, Serious and Critical.
This is handy to track together with CPU activity to see if specific parts of your game are causing your device to overheat, leading to deteriorated performance and throttling.
Top comments (0)