Question

Apple launched its new programming language Swift at WWDC14. In the presentation, they made some performance comparisons between Objective-C and Python. The following is a picture of one of their slides, of a comparison of those three languages performing some complex object sort:

enter image description here

There was an even more incredible graph about a performance comparison using the RC4 encryption algorithm.

Obviously this is a marketing talk, and they didn't go into detail on how this was implemented in each. I leaves me wondering though:

  1. How can a new programming language be so much faster?
  2. Are the Objective-C results caused by a bad compiler or is there something less efficient in Objective-C than Swift?
  3. How would you explain a 40% performance increase? I understand that garbage collection/automated reference control might produce some additional overhead, but this much?
Was it helpful?

Solution

First, (IMO) comparing with Python is nearly meaningless. Only comparison with Objective-C is meaningful.

  • How can a new programming language be so much faster?

Objective-C is a slow language. (Only C part is fast, but that's because it's C) It has never been extremely fast. It was just fast enough for their (Apple's) purpose, and faster then their older versions. And it was slow because...

  • Do the Objective-C results from a bad compiler or is there something less efficient in Objective-C than Swift?

Objective-C guaranteed every method to be dynamically dispatched. No static dispatch at all. That made it impossible to optimize an Objective-C program further. Well, maybe JIT technology can be some help, but AFAIK, Apple really hate unpredictable performance characteristics and object lifetime. I don't think they had adopted any JIT stuff. Swift doesn't have such dynamic dispatch guarantee unless you put some special attribute for Objective-C compatibility.

  • How would you explain a 40% performance increase? I understand that garbage collection/automated reference control might produce some additional overhead, but this much?

GC or RC doesn't matter here. Swift is also employing RC primarily. No GC is there, and also will not unless there's some huge architectural leap on GC technology. (IMO, it's forever) I believe Swift has a lot more room for static optimization. Especially low level encryption algorithms, as they usually rely on huge numeric calculations, and this is a huge win for statically dispatch languages.

Actually I was surprised because 40% seems too small. I expected far more. Anyway, this is the initial release, and I think optimization was not the primary concern. Swift is not even feature-complete! They will make it better.

Update

Some keep bugging me to argue that the GC technology is superior. Though stuff below can be arguable, and just my very biased opinion, but I think I have to say to avoid this unnecessary argument.

I know what conservative/tracing/generational/incremental/parallel/realtime GCs are and how they are different. I think most of readers also already know that. I also agree that GC is very nice in some field, and also shows high throughput in some cases.

Anyway, I suspect the claim of GC throughput is always better than RC. Most of overhead of RC comes from ref-counting operation and locking to protect ref-count number variable. And RC implementation usually provide a way to avoid counting operations. In Objective-C, there's __unsafe_unretained and in Swift, (though it's still somewhat unclear to me) unowned stuffs. If the ref-counting operation cost is not acceptable, you can try to opt-out them selectively by using the mechanics. Theoretically, we can simulate almost unique-ownership scenario by using non-retaining references very aggressively to avoid RC overhead. Also I expect the compiler can eliminate some obvious unnecessary RC operations automatically.

Unlike RC system, AFAIK, partial opt-out of reference-types is not an option on GC system.

I know there're many released graphics and games which are using GC based system, and also know most of them are suffering by lack of determinism. Not only for performance characteristic, but also object lifetime management. Unity is mostly written in C++, but the tiny C# part causes all the weird performance issues. HTML hybrid apps and still suffering by unpredictable spikes on any system. Used widely doesn't mean that's superior. It just means that's easy and popular to people who don't have many options.

Update 2

Again to avoid unnecessary argument or discussion, I add some more details.

@Asik provided an interesting opinion about GC spikes. That's we can regard value-type-everywhere approach as a way to opt-out GC stuff. This is pretty attractive, and even doable on some systems (purely functional approach for example). I agree that this is nice in theory. But in practice it has several issues. The biggest problem is partial application of this trick does not provide true spike-free characteristics.

Because latency issue is always all or nothing problem. If you have one frame spike for 10 seconds (= 600frames), then the whole system is obviously failing. This is not about how better or worse. It's just pass or fail. (or less then 0.0001%) Then where is the source of GC spike? That's bad distribution of GC load. And that's because the GC is fundamentally indeterministic. If you make any garbage, then it will activate the GC, and spike will happen eventually. Of course, in the ideal world where GC load will be always ideal, this won't happen, but I am living in real world rather than imaginary ideal world.

Then if you want to avoid spike, you have to remove all the ref-types from the whole system. But it's hard, insane, and even impossible due to unremovable part such as .NET core system and library. Just using non-GC system is far easier.

Unlike GC, RC is fundamentally deterministic, and you don't have to use this insane optimization (purely-value-type-only) just only to avoid spike. What you have to do is tracking down and optimizing the part which causes the spike. In RC systems, spike is local algorithm issue, but in GC systems, spikes are always global system issue.

I think my answer is gone too much off-topic, and mostly just repetition of existing discussions. If you really want to claim some superiority/inferiority/alternative or anything else of GC/RC stuffs, there're plenty of existing discussions in this site and StackOverflow, and you can continue to fight at there.

OTHER TIPS

Being 3.9 times faster than python, the language that consistently loses most benchmarks by considerable margin (ok, it's on par with Perl, Ruby and PHP; but it loses to anything statically typed), is nothing one should be boasting about.

The benchmarks game shows C++ programs that are more than order of magnitude faster than python programs in most cases. It is not much better when comparing with Java, C# (on Mono), OCaml, Haskell and even Clojure, which is not statically typed.

So the real question is how is Objective-C only 2.8 times faster than python. Apparently they carefully chose benchmark where the slow fully dynamic dispatch of ObjC hurts a lot. Any statically typed language should be able to do better. In complex object sort there are many method calls for comparing the objects and the actual comparison was probably not very complex itself. So if Swift takes at least some advantage of the type information, it can easily do better on the method calls and there is not enough other operations that ObjC could be better in.

Of course, as the benchmarks game clearly demonstrates, the relative performance on different tasks varies wildly, so one benchmark is not really representative. If they had benchmark where it had bigger advantage, they would have shown us that one instead, so on other tasks it probably isn't better or not so much.

Objective-C dynamically dispatches every method call.

Hypothesis: The benchmark uses static typing to let the Swift compiler hoist the compare method lookup out of the sort loop. This requires a narrow type restriction that only allows Complex objects in the array, not subclasses of Complex.

(In Objective-C you could hoist the method lookup manually if you really want to, by calling on the language runtime support to look up the method pointer. You'd better be sure that all instances in the array are of the very same class.)

Hypothesis: Swift optimizes reference-counting calls out of the loop.

Hypothesis: The Swift benchmark uses a Complex struct in place of an Objective-C object, so sort comparisons don't need dynamic method dispatches (since it can't be subclassed) or reference-counting work (since it's a value type).

(In Objective-C you can fall back to C/C++ for performance as long as it doesn't involve Objective-C objects, e.g. sort a C array of structs.)

Honestly, unless they release the source to the tests they are using I wouldn't trust anything Apple has to say on the subject. Remember, this is the company which switched from PPC to Intel based on power concerns when 6 months earlier they were saying that Intel sucked and actually torched the Intel bunny in a commercial. I would like to see hard irrefutable proof that Swift is faster than ObjC in more categories than just sorting.

Additionally you have to question any stats which are released at WWDC since they have the smell of marketing all over them.

All of that being said I haven't run any tests between swift and ObjC myself, but from what I know swift has it's own LLVM IR extensions and it is possible that more optimization is being done at compile time than in ObjC.

Full Disclosure: I am writing an open source Swift compiler located at https://ind.ie/phoenix/

If anyone would like to help make sure that Swift isn't just available on Apple hardware, let me know and I would be glad to include you.

I've struggled myself through the Swift tutorial, and it seems to me that Swift is more down to earth (makes me think of Visual Basic) with less 'object-ification' than Objective-C. Had they taken into account C or C++, I presume the latter would have won, since they are even more compile-time only.

In this case, I presume Objective-C is the victim of it's object oriented pureness (and overhead).

Licensed under: CC-BY-SA with attribution
scroll top