Swift is the latest hot new language from Apple. It is becoming the standard programming language on Apple systems.
I complained in a previous post that Swift 3.0 has only about half of Java’s speed in tests that I care about. That’s not great for high-performance programming.
But we do have a language that produces very fast code: the C language.
Many languages like Objective-C, C++, Python and Go allow you to call C code with relative ease. C++ and Objective-C can call C code with no overhead. Go makes it very easy, but the performance overhead is huge. So it is almost never a good idea to call C from Go for performance. Python also suffers from a significant overhead when calling C code, but since native Python is not so fast, it is often a practical idea to rewrite performance-sensitive code in C and call it from Python. Java makes it hard to call C code, so it is usually not even considered.
What about Swift? We know, as per Apple’s requirements, that Swift must interact constantly with legacy Objective-C code. So we know that it must be good. How good is it?
To put it to the test, I decided to call from Swift a simple Fibonacci recurrence function :
void fibo(int * x, int * y) { int c = * y; *y = *x + *y; *x = c; }
(Note: this function can overflow and that is undefined behavior in C.)
How does it fare against pure Swift code?
let c = j; j = i &+ j; i = c;
To be clear, this is a really extreme case. You should never rewrite such a tiny piece of code in C for performance. I am intentionally pushing the limits.
I wrote a test that calls these functions 3.2 billion times. The pure Swift takes 9.6 seconds on a Haswell processor… or about 3 nanosecond per call. The C function takes a bit over 13 seconds or about 4 nanoseconds per iteration. Ok. But what if I rewrote the whole thing into one C function, called only once? Then it runs in 11 seconds (it is slower than pure Swift code).
The numbers I have suggest that calling C from Swift is effectively free.
In these tests, I do not pass to Swift any optimization flag. The way you build a swift program is by typing “swift build” which is nice and elegant. To optimize the binary, you can type “swift build --configuration release“. Nice! But benchmark code is part of your tests. Sadly, swift seems to insist on only testing “debug” code for some reason. Typing “swift test --configuration release” fails since the test option does not have a configuration flag. (Calling swift test -Xswiftc -O gives me linking errors.)
I rewrote the code using a pure C program, without any Swift. Sure enough, the program runs in about 11 seconds without any optimization flag. This confirms my theory that Swift is testing the code with all optimizations turned off. What if I turn on all C optimizations? Then I go down to 1.7 seconds (or about half a nanosecond per iteration).
So while calling C from Swift is very cheap, insuring that Swift properly optimizes the code might be trickier.
It seems odd that, by default, Swift runs benchmarks in debug mode. It is not helping programmers who care about performance.
Anyhow, a good way around this problem is to simply build binaries in release mode and measure how long it takes them to run. It is crude, but it gets the job done in this case:
$ swift build --configuration release $ time ./.build/release/LittleSwiftTest 3221225470 real 0m2.030s user 0m2.028s sys 0m0.000s $ time ./.build/release/LittleCOverheadTest 3221225470 real 0m1.778s user 0m1.776s sys 0m0.000s $ clang -Ofast -o purec code/purec.c $ time ./purec 3221225470 real 0m1.747s user 0m1.744s sys 0m0.000s
So there is no difference between a straight C program, and a Swift program that calls billions of times a C function. They are both just as fast.
The pure Swift program is slightly slower in this case, however. It suggests that using C for performance-sensitive code could be beneficial in a Swift project.
So I have solid evidence that calling C functions from Swift is very cheap. That is very good news. It means that if for whatever reason, Swift is not fast enough for your needs, you stand a very good chance of being able to rely on C instead.
My Swift source code is available (works under Linux and Mac).
Credit: Thanks to Stephen Canon for helping me realize that I could lower the call overhead by calling directly the C function instead of wrapping it first in a tiny Swift function.
I would assume swift test is for unit tests, not benchmarks. Why would you think benchmarks are part of the tests?
I would also prefer to have benchmarks separated from unit tests…
I think Swift thinks of these as “performance tests”.
But this aside, I’d like to run my unit tests with both release and debug binaries.
Have you tried to write high-performance code with Rust? I think it could be faster than swift, and it should also have zero overhead calling C code if needed.
Since you can also have garbage collection for C and C++ there is little reason to bother with “shiny” new languages which bring brand new bugs and poor libraries.
http://www.hboehm.info/gc/
I like C and C++, a lot.
But first, they are moving targets, see http://lemire.me/blog/2016/09/14/the-new-c-standards-are-worth-it/ Standing still is just not an option in computing.
Second, these new shiny languages come with nice features like standard and universal tools to test, build and manage dependencies. Also they tend to do away with undefined behaviours. Garbage collection is just one of many small features.
Third, there are entire platforms where some language dominates. You can’t do web without JavaScript. You can’t keep doing iOS without Swift. You can’t do Android without Java. You probably shouldn’t do video games without C++.
nice post