Tuesday, June 7, 2016

Mutexes and Closure Capture in Swift

Matt Gallagher:

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 a class type to avoid breaking the pthread_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.

Comments RSS · Twitter

Leave a Comment