I have an app (Xcode project) that I developed for iOS 12 (with Xcode 10, Swift 4.2) which I now want to rebuild for iOS 13 using Xcode 11.
When I opened the project in Xcode 11, built and run the project, I found that my app was suddenly more than 20 times slower! I checked multiple times that Release configuration was selected and that the same compile flags were used as in Xcode 10. Needless to say, compiler optimisations are maxed out, just like in Xcode 10.
The app uses a lot of SIMD operations (float4, float3, float4x4, etc.) and works with pointers (e.g. UnsafeMutablePointer<float4>). I found that Xcode 11 showed me that simd types like float4 are now deprecated and typealiased to new SIMD structs.
Is there something obvious I should know about porting from iOS 12 to iOS 13? Can this change of SIMD types be the source of such terrible decrease in performance? How do I restore the original speeds (iOS 13 seems like a downgrade)?
My money is on Xcode 11. Apple probably changed the compiler, or compiler settings, in a way that affected the generation of matrix math instructions.
Apple's major releases of Xcode definitely break stuff sometimes, and it can take a while for them to sort it out.
Try building for iOS 12 while using Xcode 11. Then take the same project and build it from Xcode 10.2.
My company's app is HUGE, and build times went up by ≈3X under Xcode 11. It's quite painful. That's a different issue than execution speed, obviously, but it still tells you that there we big changes to the build process.
After I rewrote the SIMD-heavy Swift codebase to Objective-C, the old performance was restored. Objective-C thankfully still uses the good old SIMD data types without pointless wrappers.
The performance correlates with changes in Swift's implementation of SIMD. In Xcode 10.1 and older, Swift used built-in SIMD data types. With Xcode 10.2 (and newer), Apple added useless Object-Oriented wrapper for SIMD data types (e.g. float3 is typealias for SIMD4<Float>) and operations and didn't even bother to test the performance, which made my code more than 20x slower.
Sometimes you just need speed, don't force Protocol-oriented design where it doesn't belong to.
It doesn't matter if you choose to use Swift 4 or target iOS 12 in Xcode 10.2 and newer, you'll still get the same, bad SIMD performance.
TL;DR: Apple messed-up pre-Swift-5 SIMD levels of performance in Xcode 10.2 and newer. Port your SIMD-heavy code to Objective-C to get good old levels performance (>20x faster in my case). Let's hope Apple doesn't touch Objective-C or we're soon going to write in Neon assembly to get decent performance...
Related
I know Swift is not yet ABI stable, but does that only count for major versions of Swift?
Is there any guarantee that minor or patch versions (under semantic versioning) of Swift are ABI stable?
I'm guessing there is no guarantee here, but just wanted to double check if anyone has come across anything detailing ABI stability for different minor/patch versions of Swift.
Also, if I use a Swift binary framework compiled with a different version of Swift I get a compiler error usually. If I don't get a compiler error in my project does that mean it is safe, or could there still potentially be runtime issues with a slightly different (patch version) of Swift?
Update 3
We also have module stability, starting with Xcode 11, with the help of the newly introduced .swiftinterface files. One caveat, though, is that the code will have to be build with the -enable-library-evolution flag. More details here.
Update 2 Module stability is scheduled for Swift 6: https://swift.org/blog/abi-stability-and-more/#module-stability
This is an excerpt from the Swift evolution repo.
Update Swift 5 comes with some ABI stability:
The Swift 5 release will provide ABI stability for the Swift Standard Library.
Unfortunately, not yet. For Swift 4, they state this here: https://swift.org/blog/swift-4-1-release-process/.
Swift 4.1 is not binary compatible with 4.0. It contains a variety of under-the-hood changes that are part of the effort to stabilize the Swift ABI in Swift 5.
Hopefully we'll get ABI stability in Swift 5
I think we should know what is ABI stability firstly, After that your confusion has already been removed.
Today, the latest version of Swift is 3.1, so chances are if you ship an app tomorrow, your app bundle will contain the Swift dynamic libraries for 3.1, however, there are plenty of apps in the store right now which link 3.0, 2.3, and probably even some older apps that link 2.1 and earlier. Nothing is stopping me from downloading your app (on 3.1) and my app (on 2.3) and running them side-by-side on my iPhone with iOS 10.3, since both apps link against their own bundled version of Swift. It's exactly the same as you bundling Alamofire 4.4 and while I bundle 3.0.
When a language is ABI-stable (Application Binary Interface), that means it is packaged and linked with the operating system itself, in this case: iOS. The Swift code you compile on your computer has a binary interface into the operating system itself rather than any dynamic library you bundle with your application. Because of this, Apple has to be able to guarantee that my Swift code, when compiled to machine code (bitcode, LLVM-IR, yada-yada), will be able to interface properly with the rest of the operating system, and (probably more importantly) will not break between versions of iOS / Swift.
As it stands today, the Swift language specification and compiler are not in a state where the Swift team would feel comfortable making this promise of ABI-stability; changes to Swift are still too frequent and the roadmap is still too long. As soon as the Swift library is merged into iOS, it becomes much much harder to make big changes.
Why does it matter?
Yes, the bundle size of your application will decrease because you will no longer have to include the Swift standard library in your Frameworks folder, which is nice.
Language changes will be smaller / less frequent, so you won't have to worry about events like migration from Swift 2 -> 3 (I'm still scarred from that)
Developers will be able to create 3rd-party libraries written in Swift and distribute pre-compiled frameworks (binaries), because they no longer need to bundle the Swift standard library into their framework, and will instead be linking against the same version of Swift as your app (the one packaged with iOS).
This Issue Occurred On A SpriteKit Xcode Project
I have an issue with Swift where, once my project reaches a certain level of complexity where I have tons of classes and methods around, my autocompletion breaks so that "Jump To Definition" ceases to work and my IDE is reduced to the usefulness of a basic TextEditor/Notepad.
My code's color will change to white completely, which causes a lot of strain on my eyes, and this seems to only happen when I remove and add large blocks of code as I watch my CPU activity climb up to 75% usage on all CPU cores.
I think this started happening when I switched the target iOS from 9.3 over to 8.0. Is this an issue with the compiler trying to interpret old Swift syntax?
I also will have cases where I wait two minutes for minor changes to compile, only for the build to fail due to "Linker command failed with exit code", which is easily fixed by compiling twice.
Here's my specs:
OS X 10.11.4
Core i7 Skylake 4.0GHz
16 GB DDR4 RAM
256GB SSD
I'm thinking about building a system running OS X with dual processors which might alleviate the issue, since it can take upwards of 2 minutes just to compile code that's different by 1 line.
Okay, I found one solution which resolved my issue.
Remove All Emojis From Variable Names & .Swift File Names
Contrary to Apple's official Swift 2.0 textbook, DO NOT use ANY kind of Emoji's for things like Variable Names and Swift File Names UNLESS you know that the emoji you're using is a very old emoji (but even then, I wouldn't recommend it).
After opening my project in AppCode, AppCode had unicode problems with file names such as: foobar🔵.swift. It was very buggy and it would break Xcode later if I opened and saved a project with pre-existing emojis in AppCode.
So avoid using emojis in your code unless it's for strings like:
let someString = "⬛️🗡💣📚"
Welcome to Swift! Great language, awful tooling.
Joking aside, there are a couple of issues at play here. First off, syntax highlighting disappears when SourceKit crashes. SourceKit is the library which parses Swift on-the-fly so that Xcode can do things like indexing, syntax highlighting and code completion. It's a lot better than it used to be (using Swift 1.0 in Xcode was almost comical at times), but it's still far from perfect. Until SourceKit improves, you won't see much difference.
As for your build times, it's worth trying something like the Swift Build Time Analyzer to see which functions are taking a particularly long time to compile. I recently halved the compile time of the project I work on by removing all lazy vars set via closures. A recent compiler change made type inference for these sorts of closures incredibly slow, so each one was taking ~6 seconds to compile.
I have a small app written in Objective-C. It was something like 3 Mb of size.
When I added one Swift file the size of my archive grew up to 10 Mb.
What i found is that Swift embeds it's actual library in every project it is used in. This is necessary to be able to run the project even in case that Swift library changes in the future.
But 8 Megabytes is a huge overhead for small projects. Maybe there is some information about when Swift will get stable library that will be embedded in OS?
Or maybe there are some flags that can be added to the project that force compiler to use standard Swift library embedded in iOS?
Swift is still changing. So at the moment the runtime has to be included with every app. Maybe Apple will include the Swift runtime in iOS once the development of Swift slows down.
Swift source code had to be adjusted after almost every release of Xcode since the 6.0 betas. The runtime has changed at the same time the compiler has. iOS can't use a standard swift library, but has to use the one the app was compiled and linked with.
See this explanation by Apple.
you can trust that your app will work well into the future. In fact, you can target back to OS X Mavericks or iOS 7 with that same app. This is possible because Xcode embeds a small Swift runtime library within your app’s bundle. Because the library is embedded, your app uses a consistent version of Swift that runs on past, present, and future OS releases
While your app’s runtime compatibility is ensured, the Swift language itself will continue to evolve, and the binary interface will also change.
As Swift changes, those frameworks will be incompatible with the rest of your app. When the binary interface stabilizes in a year or two, the Swift runtime will become part of the host OS and this limitation will no longer exist
Not using Swift is the only way to keep your app size down.
Since Swift 3.0 won't deliver a stable ABI, this will remain the same for the time being. So in a year or two probably translates to Swift 5.0 in 2018.
So it's fairly easy to figure out what kind of CPU an iOS device runs by querying sysctlbyname("hw.cpusubtype", ...), but there seems to be no obvious way to figure out what features the CPU actually has (think VFP, NEON, Thumb, ...). Can someone think of a way to do this?
Basically, what I need is something similar to getauxval(AT_HWCAP) on Linux/Android, which returns a bit mask of features supported by the CPU.
A few things to note:
The information must be retrieved at runtime from the OS. No preprocessor defines.
Fat binaries is not a solution. I really do need to know this stuff in an ARM v6 binary.
Thanks in advance!
sysctlbyname has “hw.optional.neon”. I do not see a name for VFP, except “hw.optional.vfp_shortvector”, which is a deprecated feature.
Do a matrix float multiplaction via accelerate.framework and measure the execution time. The difference will be huge enough between Neon and VFP driven math, you simply cannot miss.
Thumb is always there, and the presence of NEON means armv7= Thumb2.
First, consider carefully whether or not you really need to support armv6 binaries for iOS. According to published version share statistics, something like 98.5% of iOS devices are running iOS 5.0 or later, which does not support armv6 devices (armv6 binaries will still run on current iOS versions, obviously, but all new apps should really be targeting armv7; there’s basically zero reason for your customers to be shipping armv6 binaries for iOS today).
Similarly, your concerns about code size are misplaced. If you provide a fat library, and your customer builds an armv6 binary against it, only the armv6 bits of your library will be built into their application. Furthermore, code size is usually a nearly trivial fraction of application bundle size; most of the size of an application comes from other resources.
Ok. All that aside, if you really want to pursue this: VFP and thumb are supported on all iOS devices, so there’s no need to check for support. You can check for NEON and thumb-2 using the method that Eric Postpischil suggested (all armv7 iOS devices have NEON support, so availability of NEON coincides exactly with availability of thumb-2).
I have to make apps for that work on both iOS 4 and iOS 5. The iOS 5.0 SDK has nice features like ARC, storyboard etc, which are not available in iOS 4.
My question is: In order to make an app optimized for iOS 4 and 5 what should do? Should I develop an app in a classic way without ARC, storyboard etc?
For instance, how can I switch off automatic garbage collection for iOS 4? If I do, of course iOS 5 will not benefit from having ARC. Also, if you mark reference as weak/string - that will not compile for iOS4, won't it?
As Andrey indicates in his comments, while automatic reference counting was introduced with the LLVM Compiler 3.0 that came with Xcode 4.2 and the iOS 5.0 SDK, you can use it in applications that target back to iOS 4.0. There's no good reason not to use it for applications that will run on iOS 4.0+. Also, it's different from garbage collection, as I explain in this answer.
__weak pointers are only available for applications that use ARC and target iOS 5.0 and greater. For iOS 4.0, you'll need to fall back to using __unsafe_unretained as a pointer type when you want to avoid retain cycles.
However, storyboarding is not available for applications targeting anything earlier than iOS 5.0. It's a nice convenience, but I don't personally use it for anything. Jonathan Wight has, and he has some complaints about its current implementation, so you might not be missing much if you gave that feature a pass.