Yalantis: iOS, Android And Web App Development Company

Is Swift Faster Than Objective-C?

Little time has passed since Swift 1.0 was released especially once you compare it with the lifetime of Objective-C which is already in its thirties. Even though Swift isn’t nearly as mature as its predecessor, the language attracts a great deal of attention, and we’re not the ones to step aside.

There are a lot of opinions out there about Swift. Some convict it in the performance issues, while others, like Apple, include performance, simplicity, and safety in the list of Swift’s merits. We can’t take anybody’s words for granted, so we decided to check for ourselves what is the difference between Swift and Objective-C, and whether Swift is really worthly. 

What did we compare?

Swift and Objective-C compilers are based on the LLVM Compiler Infrastructure, and there is a single iOS SDK for both Swift and Objective-C. That's why there isn’t much difference between the ways the programming languages work with the Cocoa frameworks.

We decided to examine both Swift and Objective-C performance by comparing their data structures. For that we took Objective-C Foundation framework and Swift’s native solutions.

We wrote several tests to estimate the performance of different types of data, such as Array/NSArray, Dictionary/NSDictionary, Set/NSSet, and defined “write,” “read,” “delete,” and “find” operations for each data type.

You can see the results of my experiments in the two projects I created – one for Objective-C and one for Swift.

I used XCode 6.3 with Swift 1.2, the latest released version.

How we estimated performance of the data structures

The most common way to measure performance is to identify which data structure needs the least time to complete a given operation. In order to find it out I considered execution time of the operations with data. But how to measure that exactly? Here is what I did:

  • I started by defining the test structure. For that I needed to place operation requests above the structure with two time notches. The first thing that came to my mind was NSdate, since it’s a basic class for any time-based operations. There are a few ways to measure execution time here. One is by using the following construction:
NSDate *startDate = [NSDate new];
 /* you can put here the code to estimate */
NSDate *endDate = [NSDate new];
NSTimeInterval operationTime = [endDate timeIntervalSinceDate:startDate];
NSLog(@"Operation takes %f ms", operationTime * 1000);

Of course, I could’ve substituted [NSDate new] with CACurrentMediaTime() for more precise results, and wrapped this construction into the method with block argument in which I would’ve sent the operation request.

But there had to be a simpler way to measure!

  • I found a better solution in an article about Benchmarking. C-function dispatch_benchmark from libdispatch library does everything for you. Unfortunately, it doesn’t work with Swift – we can’t call a private C language API.

I needed something I could use to test both languages and I found it!

  • XCTest framework is a convenient, powerful, and universal tool for both languages.

A template of the performance test for Objective-C:

- (void) testSomeStructureSomeOperationSpeed {
 /* data structure generation */ 
 [self measureBlock: ^{
 /* operation under data structure */
 }];
}

A template of Swift performance test:

func testTreeInsertSpeed() {
/* data structure generation */
 self.measureBlock() {
 /* operation under data structure */
 }
 }

As you can see, XCTest contains a very useful function measureBlock. According to the methodology behind it, you should launch the test 10 times and then, calculate the arithmetic average so as to reduce the probability of getting random results. It also calculates a relative standard deviation for series of the testing attempts.

This method seemed to be the right choice but as it turned out, it wasn’t. I’ll explain this a bit later.

In order to achieve feasible results using XCTest we needed 100,000 iterations.

The types of data we used in the operations

The next step was deciding what types of data we had to use in the operations. I chose String for Swift, and NSString for Objective-C for two reasons – these objects are often used in the app development, and resource-intensive enough to measure. I generated a unique string value for each iteration to prevent side effects.

Testing environment
Tests were run for Simulator iPhone 5 iOS 8.3.

The results

Unknown-4

Ok... what the …??! It seems that Swift is not swift at all. Array-Read speed is 10 times slower than NSArray implementation!

I decided to find out what the problem was.

The reason why I received such disturbing results was that I ran these tests in the default test scheme with debug build configuration in the XCode. There is no compiler optimization in the debug mode by default, however, so the compiler handled the task quite quickly but also quite inaccurately.

Optimization level flag is located in the Build Settings of the target.

OptimizationLevel.jpg

I changed the mode to Release (-Os optimization flag) and tried conducting the test again.

Unknown-5

Much better, isn’t it? The data in Objective-C practically didn’t change, but it did in Swift, and a lot. Why is Swift more affected than Objective-C?

The reason is static type checking in Swift and a dynamic nature of Objective-C.

In Swift, all classes are created during compile-time. Methods cannot be added on-the-fly and all types are known before the run time. Since everything is known beforehand, a compiler can optimize code without any problem.

Objective-C, on the other hand, can’t optimize as effectively, because all dynamic languages work slower than static.

Here are the results I got after the optimization:image-8.png

image-7.pngimage-6.png

  1. Operations Add, Update, and Read in Swift’s Array are much faster than in NSArray.
  2. Swift is NOT slower than Objective-C! Well, it’s obviously not 3.6 times faster as Apple claims but not so slow either.
  3. There is an anomaly in the Array’s “delete by index” results: Deletion of data in Swift is 10 times slower than in Objective-С.

Why XCTest is NOT the best way to measure performance

Even though I got the results I needed, there were some questions I had no answers to. The most important issue that I couldn’t resolve was examination of the hypotheses about the algorithmic complexity of the operations over data structures.

Since there was no way for me to test that hypothesis, I also didn’t get any results which could illustrate the dynamics of the operation performance with regards to the number of elements contained in a given instance of a data structure.

What’s more, the accuracy of the results obtained isn’t high enough to make any conclusions about the performance. This test embraces several side operations which could distort the results of the experiment. Since the operation was carried out 100,000 times over one data structure, you can only imagine how hard it is to define what exactly made a major impact on the performance.

After I had talked to other developers in our company, I decided to take an absolutely different approach to measuring performance. You can find it in the Master branch of same repositories I mentioned above (for Objective-C and for Swift).

A more effective approach for measuring performance

Since XCTest doesn’t offer sufficient flexibility and accuracy for measuring execution time of nanosecond operations, my next experiment excluded this test completely.

The new approach to measuring performance implied experimenting with a pre-initialized data structure filled with a fixed number of elements (initial states). We did only one operation over a data structure, then created a new structure with a new initial state, and executed the operation again. We considered 500 states per each data structure, and calculated their performance over 10 iterations.

For example, at first we recorded the current time of the operation with Array’s "0" state. After we created a new empty Array and executed the operation, we measured the time again. Then, we repeated the same operation with new Arrays 10 times and calculated the average arithmetic value. We carried out the operation 10 times for the same reason we did the XCTest 10 times – to reduce the probability of a random impact. These 10 attempts gave us the resulting average value which we used for building the graphs of functions.

As mentioned above, we carried out the same procedure for all 500 states of all data structures and displayed that in a graph of a function, where X is the initial state of an Array, and Y is the average execution time for a given Array.  

The results

The tests can be found in the Master branch in the repositories here and here.

Array-Add.png

  1. Adding the first element to the dynamic Array in Swift is four times faster than in Objective-C.
  2. The operation is performed in constant time in both languages.
  3. For filled Arrays the operation in Objective-C is performed two times faster than in Swift.

Array-delete.png

  1. There is a significant gap in the performance of Swift and Objective-C when deleting one element from an Array.
  2. Swift perfoms deletion in the Array two times faster on the average than Objective-C.
  3. The Array-Delete operation in Objective-C is linear initially with transition to the constant time in the end of a given section. Whereas for Swift the complexity of the operation is linear throughout the entire interval.

Array-Read.png

  1. Swift is 4-6 times faster.
  2. Both languages perform an operation in constant time, but Swift is much more stable compared to Objective-C.

Array-Contains.png

  1. Linear complexity for both languages is absolutely obvious.
  2. Swift a little outstrips Objective-C.

 

Array-Update.png

  1. Swift is 3-4 times faster.
  2. In Swift the operation has a clearly defined constant complexity, whereas in Objective-C the function is polynomial and becomes constant only in the end.

Dictionary-Add.png

  1. Swift is 2-3 times faster.
  2. We can notice regular peaks of time consumption in Swift, which repeat after ever increasing intervals. It can be explained by the need to allocate memory for new elements, since the size of Dictionary increases according to the function 2x, and not linearly.

Dictionary-Contains.png

  1. The situation is similar to the Array-Search – both languages provide linear complexity, but for this structure Swift has a lower performance.

 

Dictionary-Delete.png

  1. Objective-C is 3-4 times faster for this operation.
  2. Orange curve (Swift) has a wavy structure. Most likely it’s connected to the dynamic change in the size of the Array.
  3. Both graphs seek constant time.

Dictionary-Read.png

  1. Objective-C is two times faster for this operation.
  2. The complexity of both graphs seek constant.

Dictionary-Update.png

  1. Quite surprisingly, Swift is two times faster, than Objective-C.
  2. Algorithmic complexity for Swift seeks constant. For Objective-C the graph of a function is linear with the minimal inclination.

Set-Add.png

  1. We have similar peaks to those in the Dictionary-Add graph.
  2. Swift is two times faster.

 

Set-Contains-2500.png

  1. When considering long intervals with 2500 elements it becomes clear that graphs almost coincide.
  2. There is an insignificant linear trend.

 

Set-Delete.png

  1. Objective-C is 2-3 times faster.
  2. Both graphs demonstrate constant complexity, but Swift's graph is much more prone to random fluctuations.

What conclusions can we make?

  1. The operations with Array are 2-4 times faster than the operations with NSArray.

  2. To increase performance, you should pre-initialize Dictionary and Set with the maximum number of elements known in advance.

  3. All operations except search (*Contains) seek constant time, which proves high effectiveness of the algorithms used in these data structures.

  4. Even though Swift handles insert operations in Dictionary and Set much more effectively, it doesn’t provide the same efficiency for other operations over these structures if compared to Objective-C.

  5. Array in Swift is preferred over other types of data for all operations, except Search if you have a large number of elements. In this case, Set will be the most optimal solution. Dictionary is absolutely ineffective. You should only use it if there is no alternative.

  6. As you noticed, graphs of functions look pretty weird for a pure C-Array. The reason is that it’s actually not C-Array at all. Objective-C uses complex inner data structures which aren’t Array by nature, but perform the Array functionality. Read more about it here.

Which language is better?

We compared the performance of Swift and Objective-C, but we haven’t yet talked about coding. There is a number of criteria I use to define the quality of language. They are simple and clear syntax, smart and helpful compiler, and safety that results in fewer bugs.

I compared the languages by writing the following data structures: LinkedList, Stack, Queue, and Binary Search Tree. I am also writing several apps which help me get the feeling of the new language. So here is what I can say about Swift.

Swift vs Objective-C: Pros & Cons

Here are the results of our comparison.

Pros:

  1. Swift has a lot of cool things, such as safe memory management, strong typing, generics and optionals, simple but strict inheritance rules.

  2. Swift is cleaner and more readable than Objective-C. There are modules that eliminate class prefixes. It also has half as many files in a project, and understandable closure syntax.

  3. Swift allows to create flexible and lightweight classes which contain exactly what you want (no root class), i.e. if you want to print description, just implement the protocol Printable and if you want to compare -  implement Comparable.

  4. Swift isn’t that fast, but isn’t slower than Objective-C either.

Cons:

  1. Compiler provides misleading and confusing errors (while typecasting and implementing generics, for example).

  2. Poor third party IDE support – AppCode still doesn’t have code generating and on-the-fly analysis, not mentioning simple code completion.

  3. Native IDE - XCode has a lot of bugs. For example, the latest released XCode 6.3 version doesn’t recognize Swift’s unit tests, so UI test buttons don’t come up. What’s even more disappointing is that this function worked in Beta 4.

Swift made a big step forward since its initial release. The language is ready for commercial use in small and middle-sized projects. But the process of app development in Objective-C is still faster than in Swift.   

 
Tech

Declarative Navigation Bar Appearance Configuration

Tech

How to Achieve Loose Coupling?

Design

UIDynamics, UIKit or OpenGL? 3 Types of iOS Animations for the Star Wars

See what else we can do

Check out our knowledge and capabilities

Let's talk code