My demo completely failed (Mac locked up when connecting to the projector) so I couldn't show what I wanted. If you want to try the demos yourself these are the Github branches I was going show. All these are 70 pixel square calculations for consistent comparison the main master branch increases in later versions.
While GCD is a fairly simple API enabling blocks to be dispatched asynchronously on different threads or on particular queues that doesn't mean that the API can't be cleaned up a little to make it really nice to use with Swift.
Tobias Due Monk released the Async library that does just that. I saw the announcement probably on iOSDevWeekly and I liked what I saw except there was one fatal issue for me in that the library required iOS 8 or OS X 10.10 and I haven't updated my devices yet let alone moved my target up to that version yet. I actually just left it and ignored it but having done the optimisation of the GrayScott code presented at the last Swift London meetup I felt comfortable enough with GCD that I could implement most of the fuctionality to work with iOS 7 (and OS X 10.9 Mavericks but just take that as read every time I mention iOS 7 in this article). The result was Async.Legacy
How to Use Async (or Async.legacy)
All you need to do is to drop in the Async.swift (or AsyncLegacy.swift if you are using iOS7) into your project and then you can call the methods. There is full API documentation in the Readme files and that are easily viewable on the Github pages but essentially what Async allows you to do is chain a series of Asyncronous calls rather than to nest them. The result looks like:
Instead of having to nest the calls like this:
Having spent a couple of evenings back porting Async to iOS 7 and OS X Mavericks (10.9) and releasing it as Async.legacy I've gone back to trying to squeeze some more performance out of the GrayScott Cellular Automata app that Simon Gladman presented at the last London Swift Meetup.
For me this was an interesting case to see how fast something that is almost entirely CPU and memory bound can be made and it gave me a chance to play. Not many things need optimising if well strutured but this was a case where it could clearly be relevant.
Simon's original code calculates about 10fps in debug mode and displays many of them. Built with optimisations it increases to about 41fps calculated but it very rarely updates the screen due to the timing mechanism he used rather than calling back to the main thread. All this was done on a 70 pixel square calculation.
Running the latest code on a 70 pixel square calculation it calculates between 1550 and 1600 frames most seconds for a speedup of about 40 times and it is displaying far more frames to the screen too (well assigning the images to the image property of the imageView, the screen framerate is far lower).
This post focusses on making the main solving work multi-threaded for performance and in the optimisation of the inner loop. At this point we are moving beyond the point where we are optimising by improving the style, purity and immutability of the code. Some of the changes (inlining simple functions) go directly against good style and should only be done in inner loops. The parallelisation of the main solver is also something which makes the code less clean and tidy as is the incorporation of the pixelData generation into the main loop.
This can either be read as a follow up to my last post about improving a Cellular Automata demo created by Simon Gladman (aka FlexMonkey) and speeding it up or as a standalone post with simple example code for creating images (UIImage or CGImage) from raw pixel values.
I had reached the point where the rendering code was the bottleneck in the Gray Scott Cellular Automata app that I was optimising. The existing code was drawing a set of one point rectangles into a UIGraphicsImageContext
I could see in the profiler that the execution time was being dominated by the drawRect calls which didn't surprise me and I knew that there must be a better way to draw pixel data.
Solution - CGDataProvider and CGImageCreate
This is the core generally applicable function that anyone can use to create images quickly from pixel data.
At yesterday's Swift London Meetup Simon Gladman (aka @FlexMonkey) presented the Gray Scott cellular automata application he had been developing to explore threading in iOS using NSOperation. During the presentation there were a couple of things that were apparent and looked possible to improve on. Firstly Simon had used a timer to work around a difficulty that he had in calling back onto the main thread and secondly he found that he got better performance using an NSMutableArray than using Swift Arrays. When I got home I forked the repo and got to work. This post describes the changes I made. The bulk were made together in parallel before I even ran the code but I will break down the changes.
This post describes the significant changes that resulted in needing less code, being clearer (at least in my view) and actually speeding up some sections by about 18 times. This speedup is in particular array processing code and largely the result of changing from NSMutableArray to a Swift array of structs which should be accessed with much less indirection. This improvement wasn't a direct path and if you browse the branches in my fork of the Repo you can see some dead ends and some of the steps along the way. The changes I'm discussing in this post can be seen in the pull request.
I've just read Andrew Bancroft's post about being explicit about types in Swift which I largely agree with and is worth reading before the rest of this post which adds an extra reason to follow his advice.
Summary of Andrew's Post
Where the type of a declared variable is not immediately visible from the surrounding code such as when it is declared to equal the result of a function, possibly a function defined in another file, you should explicitly declare the type for human readability and understandability purposes even the the compiler is happy inferring the variable's type. I agree with this.
The Other Big Benefit
That post however misses an entire other benefit of the practice which is that you get the compiler to check for you that the type that will be assigned is he one that you expect. If the function you are calling gets changed you will get an error at the call site not where you first use the object of inferred type (if you are lucky - you may get no error at all if the object is used in locations where type is liberal such as string interpolation).
Consider Defining Your Variable as a Superclass or Protocol
Now it may be possible to declare your variable to a superclass or protocol of the expected object's type and if so that should be done to give some flexibility while ensuring expectations are met. This also documents the flexibility of the type that you can accept.
Drawback of Explicit Typing
The disadvantage of explicitly typing your object is that if you change what the function that sets it returns and it is still compatible you have to manually change the type (possibly in many places if it is a heavily used function). I think that is a price worth paying to encourage at least a cursory check of the use of the values and to ensure it is not just well typed (which the compiler can do) but also correct.
The Apple documentation is I believe correct although in places it isn't as explicit as I would prefer. This short article aims to explain what you can and cannot do in the background and the behaviour you will see. This information is relevant for iOS 7. [Update:
If iOS 8 is different I will try to revise this post at a later date. iOS 8 behaviour appears the same in my initial testing and I couldn't find any significant changes documented and there was no CoreBluetooth talk at WWDC 14.] If you think anything is inaccurate please let me know I don't want to mislead anyone and my testing hasn't been extensive.
Not Quite Enough for Peer to Peer Applications
My summary of the situation is that you can't do quite enough to support peer to peer in a viable way unless you have an app that you expect to be run on a regular basis anyway because the background modes (except for iBeacon detection) do not persist through a reboot of the phone or a flat battery. This means that the user will fall off your peer to peer application framework.
iBeacons can awake apps that have not been running since a reboot but iOS devices can only themselves perform as iBeacons in the foreground.
Apple introduced access controls to Swift with the release of Xcode 6 Beta 4 and accompanying documentation (revision history).. The approach they have taken contrasts strongly with that taken by languages like C++, C# and Java in that the controls do not limit access based on the class hierarchy but instead limit based on the file and module.
Even variables declared private can be accessed from other classes and functions that reside within the same file. This allows properties and functions to be declared private but still accessible to free functions and operators defined particularly to operate with that class. It avoids some of the complexity that has C++ having "friend" classes and functions.
I should probably have written this sooner as it has been 10 days since Beta 3 was released, but I've only just noticed that there is a bit of traffic still coming in about Swift Arrays from my earlier posts about the array semantics in the first Swift betas. The short version is that I'm very happy with the new Swift Arrays in Beta 3 which have full value semantics (optimised with copy on write under the hood). The syntax for declaring arrays has also changed which is fine although I didn't have a particular problem with the old syntax. Be sure to update the Swift iBooks (delete and download again) as there are some significant changes.
The copy() and unshare() functions are gone because unshare has essentially become the default behaviour (although contents changes not just length changes now trigger the copy so copy is not needed either).
- Arrays are no longer reference types. You can treat every assignment, argument pass and function return as if it were a copy.
- Changing the contents of an array is no longer permitted when the array is declared constant with let.
In this example you can see the copy-on-write (CoW) behaviour using the identity operator (=== which I wouldn't recommend you doing in real code to affect behaviour on the CoW status but it enables us to observe the CoW). In the example it doesn't make any difference if a is declared with let or var but b must be declared as var otherwise you will get a compile time error because the contents change.
I don't know and I don't really care if it was always the plan or is a result of feedback. I think this is very good news.
I think that this means that it will be copy on write behaviour so that if you aren't changing the array you get pass by reference performance and only pay the call by value price when a mutation will occur. It may even be smart enough to know when the caller isn't going to access the array again and allow it to pass and mutate the original. We'll see in beta 3 (or maybe later).