DEV Community

Attilio Carotenuto
Attilio Carotenuto

Posted on • Edited on

Native iOS Unity Profiling using XCode Instruments

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.

Image description

Image description

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.

Image description

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:

Image description

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.

Image description

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:

Image description

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.

Image description

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.

Image description

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.

Image description

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.

Image description

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.

Image description

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.

Image description

You can select each individual track to retrieve more granular information about it. For example, here’s the Time Profiler.

Image description

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.

Image description

You can quickly inspect the stack traces taking the most time by looking at the pane on the right.

Image description

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.

Image description

The Display Track

The Display track allows you to monitor the average game framerate, tracking each frame duration.

Image description

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.

Image description

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.

Image description

The Allocations track

This track will show all heap and virtual memory allocations, grouping them by category, and record reference-counting events.

Image description

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.

Image description

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.

Image description

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.

Image description

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)