I held off writing this post since Apple’s new Swift language was at version 1.0, and it was clearly rushed to market/probably should’ve stayed in beta for quite awhile longer, but now that 1.1 is out, I can say that things are not looking good from a performance point of view. The reason I was excited about the language from the moment I read the specification (other than wanting to see the end of Objective-C) is that the design takes into account the notion of having a clean high level syntax that provides enough information for the compiler to understand what is, and isn’t, trying to be accomplished in a given block of code, and optimise accordingly. A reading of the spec suggests that Swift is, among other things, a better Java: all the safety and convenience of bounds checking and automatic memory management, but with clever ways to reduce or eliminate the performance penalties that come with it.
There are a number of benchmarks you can find out there on the internet, and they generally don’t look very good, but a lot of the chatter about performance relates to rate limiting steps that rely on calling class libraries, for example sorting. This is not especially interesting, and it’s also a great way to tell an untruth without actually lying: if you make NSArray better at sorting, you can publish a benchmark that is completely and utterly bogus, and claim plausible deniability. I suspect that some of those graphs shown in the September 2014 presentation may be of this nature.
I am still convinced that the fact that Swift is designed as a theoretically performant high level language is compelling, because it means that Apple can spend the next 10 years improving the compiler, and the apps will just keep on getting better, and nobody will need to change a line of code to benefit from it. This is really important, because most high level languages are slow by design, and there’s just no way to do anything about it, other than restructuring fundamental design decisions and turning it into something else, breaking everything that’s out there. Some languages have no escape clause (e.g. JavaScript), while others let you plug in low level languages (e.g. Python + C libraries), but the one thing that makes a language like Java my first choice is the fact that it makes it possible to use the same language for high level business logic, low level algorithms with rate limiting performance, and everything in between, simply by using a different coding style, rather than a completely different tool. There is a great deal of room for improvement, but it’s mostly details rather than design, and the alternatives fall short with a much smaller domain of practical use cases.
My first foray into Swift was to create the Beer Lab Notebook app from scratch, to get some experience. It worked out quite well, and I was moderately pleased with the result. The app itself does not do any grinding calculations, and the rate limiting steps are primarily to do with API calls, though in spite of this I did notice it to be a little bit more sluggish than what I have come to expect from similar types of functionality from my now fairly extensive experience with Objective-C apps.
Moving on, I proceeded to implement something a bit more cerebral: a chemical substructure matching algorithm that implements a highly typed subgraph search. The algorithm was ported directly from Java, and it was originally optimised for that platform, i.e. limiting to primitive scalar datatypes whenever possible, liberal use of the final keyword, avoiding resizing arrays, and generally minimising use of new to ensure that garbage collection does not become the rate limiting step. The analogous Swift implementation should, in principle, benefit from all of the same optimisations, with some of the must-haves in Java being nice-to-haves in Swift (i.e. same or better). The algorithm itself involves a lot of looping over arrays of integers, looking up arrays by index, a bit of string handling, and a tiny bit of floating point arithmetic, but for the most part it’s a lot of condition evaluations and branches. The kind of thing that can be mapped very directly to assembly language, and so a good low level compiler can make it blaze, while a high level scripting language can be expected to introduce overhead to the tune of orders of magnitude of slothfulness.
I wonder if you can guess which of these two extremes the outcome from the Swift version resembles?
Depressingly it is the latter. The test case for the Java implementation goes by in an eyeblink. On a fast Intel CPU using the simulator, the full debug test case takes about 300 seconds to complete the Swift implementation, while switching on all optimisations (and disabling safety constraints, if I’m not mistaken) reduces it to 95 seconds. At this point there’s no point in giving real numbers for the Java version (less than a second), because the slowdown is more than 100x, and once we start making comparisons on a log scale, there’s not much point in quibbling about the exact timing. Especially for code that is intended to be deployed on a mobile device.
When I originally measured these benchmarks, I was not too worried, because it’s fairly standard that when coding up performance sensitive routines in a new language/framework, there is generally a few false assumptions about performance profiles (e.g. maybe you thought array resizing or measuring string length was O(1) but they turn out to be O(N): these are two of the classics, but there are many more). But after some heavy digging, timing, profiling, etc. it turns out that there isn’t a specific performance problem: everything is slow.
By all means take this article with a grain of salt: I’m not disclosing my algorithm (it’s proprietary), but I’ve been doing this for awhile, and I know what symptoms to look for. I really want Swift, or something more modern than Objective-C, to succeed, because every year I’m becoming a bigger fan of what Apple is doing. But if you’re considering jumping into Swift, and you’re writing code that’s more performance sensitive than a trivial webapp, you might want to run some benchmarks first. Or wait a year. I’m pretty confident that the situation will get better, but I have no way of knowing how long it will take. I would imagine the compiler team is working pretty hard now that the spotlight is shining on them.
As an addendum, I just finished porting the same algorithm I was referring to above “down” to Objective-C. The blow-by-blow sequence is identical to the Swift implementation, and while the rate limiting steps do mix in some low level plain C, there is still plenty of overhead being siphoned into the object oriented message passing scheme, which theoretically should be free in Swift.
The validation test for the Objective-C version takes about 2 seconds, as compared to the Swift version that took 300 (debug) or 95 (unsafe). That’s either a 50x or 150x slowdown for a real world algorithm with no funky gotchas and no real excuses for being slow. So that’s how much fat that needs to be burned off before we can start using it seriously…
Um, what compiler flags did you use?
HI Alex, excellent read, thanks. Coming from a Java background myself, and being familiar with a few more, it took me about 2 weeks to drop Objective-C in 2008- programmers really do have linguistic tastes as I discovered first hand. So with Swift, while I can see its polylingual influences, I am still skeptical, but, for me, it is a lot more pleasant than Objective-C. I’ve only played around with it lightly so far, so I will most definitely bear your notes in mind moving forward, especially with regard to performance. I really really don’t want to resort to building in foundational hacks or tweaks to gain speed during early stage development, so hopefully at later stages, I’ll discover some recipes to at least alleviate any issues here if they pop up. That alone will be interesting and I will seat that at the back of mind if I decide to commit to Swift. Thanks again Alex.
Based on industry precedents, I would say that Swift is about 3-4 years away from being ready (i.e. reaching the point at which a sane person who had a real choice would seriously consider it as an alternative to one of the other mature options out there). But considering that it’s being pushed by Apple as a make-or-break, I wouldn’t be too surprised if it got really good much quicker than that. Only problem is that and several million software engineers, including myself, need it to be there today. But isn’t that always the way it works…
Ha ha- yep- Java 6.*cough*…