It’s important in any type of programming language to know how to measure the performance of code as there are many different ways to write solutions and not every solution is as performant as the other. If a piece of code turns out to be slow in, for example, the results of the Time Profiler in instruments, you need a way to improve it.
I’m curious, what do you think would perform better?
Looking at this tweet:
Improvement starts with measuring the baseline so you can actually tell if a new piece of code performs better. A good way to do this is by making use of the measure
method available in XCTest
. You can make use of this method in three different ways:
- Writing a unit test in an Xcode project
- Writing a unit test in a Playground
- Writing a unit test and execute it in the terminal
You might be surprised, but the results over every method are not the same.
Although Playgrounds are easy to set up and give you results very quickly, it’s way less performant.
The code we’re going to measure
In this example, we’re going to measure the performance fetching even numbers from a big collection of numbers. In one scenario we make use of a for loop combined with an if statement. In the other scenario, we’re going to make use of a filter combined with a for each loop.
import XCTest
class PerformanceTests: XCTestCase {
lazy var testData: [Int] = {
return (0..<100000).map { Int($0) }
}()
func testForEachLoop() {
measure {
var evenNumbers: [Int] = []
testData.filter { number in number % 2 == 0}.forEach { number in evenNumbers.append(number) }
}
}
func testForLoop() {
measure {
var evenNumbers: [Int] = []
for number in testData {
if number % 2 == 0 {
evenNumbers.append(number)
}
}
}
}
}
/// In case of a Playground:
PerformanceTests.defaultTestSuite.run()
Running test code from the terminal
Although running this code from a Playground or Xcode project should be fairly simple, running it from the terminal is a bit less familiar for most of us.
The code needs to be compiled first before it can be executed from the terminal. You can do this by pointing to your Xcode installation combined with the swiftc
command.
swiftc -Onone -F /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks/ -Xlinker -rpath -Xlinker /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks -lswiftCore Performance.swift -o performance
Performance.swift
is our input filename and -o performance
is our output filename. After executing this line in your terminal you can execute the Swift file by using ./performance
.
The results
Type of code | Xcode Unit Test | Playground | Terminal |
---|---|---|---|
For loop if | 0.026 | 26.052 | 0.030 |
Filter forEach | 0.043 | 2.116 | 0.047 |
We can conclude that measuring the performance of code can be done is by making use of unit tests inside an Xcode project. Although Playgrounds are easy to set up and give you results very quickly, it’s way less performant.
Reasons for the Playground to be slow is related to the UI updates to be done as pointed out by Paul.
Both Terminal and unit tests in an Xcode project give more or less the same results. Taking into account the overhead of running tests in the Terminal the best way to measure the performance of a piece of code is by making use of unit tests in a normal Xcode project.