Secret of Swift Performance

Part 2 — Look under the hood


Secret of Swift Performance

Part 1 — Measure

Look under the hood

When you want to analyze an app performance, definitely your best friend are — Instruments and Measure. I hope everyone knows Instrument and have used it at least once. Instruments provides great tools for giving you answer: “How much memory does my app use”, “How fast it”, “Is it leaking memory”, “How …” and etc.
But as an Software Engineer we also want to know “Why … ?”,
“Why it happens?”

When I worked with Swift, I saw interesting results I couldn’t understand. “Why this code is so fast?” To answer this question I had to look at the Assembly code it compiles to. It wasn’t so hard and I find it very useful. And I’ll show you how to do it.

Compile Swift code

We want to compile and analize just some pice of the code, not whole project. To do this you need :

  • Create new Swift file.
  • Create a simple function `func test() { … // your code here }`
  • Paste the code you want to compile and analize to the function body
  • Call the function

What we did here is — We have declared a test function with a body we want to inspect and analize. We also need to call it and we do it at the top level

Now we want to compile this file. We can do it with:
xcrun - Xcode Tools and swiftc - Swift compiler command line tool.

  • Open terminal and navigate to the Swift file you have created
    use cd command for that
  • run ‘ xcrun swiftc -Onone inputFile.swift -o resultFileName’
    example: xcrun swiftc -Onone Assembly.swift -o result

This command will compile 1 Swift file you provided.
You can get full list of Swift compiler with - xcrun swiftc -help. The most interesting for us at the moment are Optimizations options:

-O, Compile with optimizations
-Onone, Compile without any optimization
-Ounchecked, Compile with optimizations and remove runtime safety checks

It’s important to use -O option. This is the same as Release mode when you compile your app. Usually you want analyze your code that will be shipped to AppStore.
For the test we will use -Onone mode, because it will generate Assembly code very similar to our source code. It’s also useful to generate both -O and -Onone version of your file, and see the difference. This way you can learn how Swift Compiler does optimization.
run - xcrun swiftc -Onone Assembly.swift -o none

As a result you get Executable File. You can double click it and run.

When you compile 1 Swift file, the Swift Compiler does those things:

  • It creates a console app with int main(int arg0, int arg1) function. This is the start point of the app.
  • It create _top_level_code function. The body of that function is the top level executable from your swift file. In our case it’s just test() function call

Getting Assembly code

There are many ways you can get Assembly code. I would suggest using Hopper Disassembler app. Download it and use fee Demo mode. The best thing about Hopper is that it can show Pseudo Code of Assembly. It’s way easier to work with it.
Let’s get Assembly code:

  • Open Hopper > File > Read Executable to Disassemble, and select executable file (the file generated by the Swift Compiler) > Click Ok

Quick Hopper Overview

The Hopper app is similar to the Xcode, on the left is navigation panel, in the senter Editor, on the right Help and Inspector panel.

Left panel — Here you find all function, string labels and string, you can click on them to navigate to he corresponding Assembly code.

Editor — Shows you Assembly code, it’s the same as Xcode swift or any other. You can use arrow to navigate

Analyzing Code

First we need to find Entry Point, in our case it’s _main function. Select it in the Navigation panel. This is Assembly code of _main function.

Assembly code is hard to analyse, but Hopper can generate Pseudo code.
Click “Alt + Enter” or Window > Show Pseudo Code of Procedure. Now you can see Pseudo code for _main function

This is a way better!!
In first 4 lines it extracts it’s parameters, this is not interesting for us. Then it calls _top_level_code(); As I mention before there should be our code. Let’s go and see it. Close pseudo code view, select _top_level_code function and show pseudo code for it.

It has just 1 function call _TF4none4testFT_T_();
Swift generated functions has special naming convention. In consist of module name + function name + count of characters + parameters type + other stuff. Mike Ash wrote about it in details.
We can see none - our file name, test - function name. But this we can say that this is out test() function. Let’s inspect it. Search for __TF4none4testFT_T_ and show pseudo code.

It has 3 variables, in Hexadecimal format:

var_8 = 10,
var_10 = 10,
var_18 = 20

This is really similar to our Source code, but there is plus operation, Swift calculate result directly at compile time.

var x = 10
var y = 10
var c = x + y

Now you know how to: Compile Swift code, Disassemble it, and Analize Assembly code. You can learn and discover many interesting thing by looking on it. For comparison let’s now compile the same code with -O mode and see how Swift Compiler optimize code.

Do the same same steps as about but now Compile with -O mode run — xcrun swiftc -O Assembly.swift -o optimized

As you see, there no method call in the main function. There is no _top_level_code. No call to out test() function.
Swift Compiler detect that result of test funtion is not used, so it skip it, because there is just 1 function call in out app, it skips it as well. As result we get empty main method!

This “Part 2 — Look under the hood” teach how to use tools to analize code.
I’ve discovered many interesting way to improve Swift Speed in that way and I’m going to tell you about the in Part 3.
Part 3 - Coming Soon…

Want to Learn More?

Check out Chapter 8: “Discovering all underlying swift power” in
Swift Hight Performance book