Mutexes and Closure Capture in Swift
Using a mutex in Swift isn’t particularly difficult but I’m going to use the topic to highlight a subtle performance nuisance in Swift: dynamic heap allocation during closure capture. We want our mutex to be fast but passing a closure to execute inside a mutex can reduce the performance by a factor of 10 due to memory allocation overhead.
[…]
One of the advantages with closure capture is how effortless it seems. Items inside the closure have the same names inside and outside the closure and the connection between the two is obvious. When we avoid closure capture and instead try to pass all values in as parameters, we’re forced to either rename all our variables or shadow names – neither of which helps comprehension – and we still run the risk of accidentally capturing a variable and losing all efficiency anyway.
[…]
It turns out that despite being a private function in the same file – where Swift can fully inline the function – Swift is not eliminating redundant retains and releases on the the
PThreadMutex
parameter (which is aclass
type to avoid breaking thepthread_mutex_t
by copying it).We can force the compiler to avoid these retains and releases by making the function an extension on
PThreadMutex
, rather than a free function[…][…]
But despite the speed increase, using a semaphore for a mutex is probably not the best approach for a general mutex. Semaphores are prone to a form of priority inversion.
Long-term, Swift should be able to keep @noescape
closures on the stack.