One of the upsides of having FastAI in Swift would be the ability to better utilize multithreading than what can be accomplished in Python, however, while I was working on a small project, I encountered extremely poor performance (multiple times slower) trying to add multithreading to it to speed it up. The single threaded version far outperformed my multithreaded version even when the multithreaded version was utilizing all seven of my i7 cores. In my application the threads were sharing a common array (struct) with each thread just reading the data and performing some other work based on the array’s contents. So no writing and no added synchronization since no updates. However, for some reason I have yet to figure out this results in the extremely poor performance. By having each thread make it’s own copy of the array or passing a copy to the thread, the issue is resolved. In looking at the Swift source, I don’t see any type of threading synchronizations going on but I do see a lot of checks for reference uniqueness but that uses a Builtin that i haven’t yet cracked open to try to understand. I mention this issue because I would assume that any multithreaded implementation of FastAI may shared DataSources, DataLoaders, etc. and may suffer the same ill-fate. If anyone knows the underlying cause of this issue, that would be great.
Here is a simple sample program which illustrates the issue:
import Foundation
class SomeThread : Thread {
private let numbers: [Double]
public init(numbers: [Double]) {
self.numbers = numbers//.map { $0 } // Remove the first comment to fix the issue
}
override func main() {
// do some calculations
for loop in 1...5 {
var sum = 0.0
for num in numbers {
sum += num
}
var min = numbers.first!
for num in numbers {
if num < min {
min = num
}
}
var max = numbers.first!
for num in numbers {
if num > max {
max = num
}
}
var squareRoots = [Double]()
for num in numbers {
squareRoots.append(num.squareRoot())
}
print("Loop \(loop) done.")
}
}
}
var numbers = [Double]()
for _ in 1...1_000_000 {
numbers.append(Double.random(in: 0...1))
}
var threads = [SomeThread]()
for _ in 1...10 {
let thread = SomeThread(numbers: numbers)
threads.append(thread)
thread.start()
}
let start = Date()
outer: repeat {
for thread in threads {
if !thread.isFinished {
Thread.sleep(forTimeInterval: 0.01)
continue outer
}
}
break
} while true
let end = Date()
print(start)
print(end)
Here’s the output with the issue. Notice how all the loops coincide with no overlap of loop output. This took almost 2 minutes to run.
root@27e01c28a48d:/swiftthreads# swift run
Loop 1 done.
Loop 1 done.
Loop 1 done.
Loop 1 done.
Loop 1 done.
Loop 1 done.
Loop 1 done.
Loop 1 done.
Loop 1 done.
Loop 1 done.
Loop 2 done.
Loop 2 done.
Loop 2 done.
Loop 2 done.
Loop 2 done.
Loop 2 done.
Loop 2 done.
Loop 2 done.
Loop 2 done.
Loop 2 done.
Loop 3 done.
Loop 3 done.
Loop 3 done.
Loop 3 done.
Loop 3 done.
Loop 3 done.
Loop 3 done.
Loop 3 done.
Loop 3 done.
Loop 3 done.
Loop 4 done.
Loop 4 done.
Loop 4 done.
Loop 4 done.
Loop 4 done.
Loop 4 done.
Loop 4 done.
Loop 4 done.
Loop 4 done.
Loop 4 done.
Loop 5 done.
Loop 5 done.
Loop 5 done.
Loop 5 done.
Loop 5 done.
Loop 5 done.
Loop 5 done.
Loop 5 done.
Loop 5 done.
Loop 5 done.
2019-05-24 19:47:11 +0000
2019-05-24 19:49:07 +0000
With the fix, this runs in 14 seconds and notice how the loops numbers start to interweave.
root@27e01c28a48d:/swiftthreads# swift run
Loop 1 done.
Loop 1 done.
Loop 1 done.
Loop 1 done.
Loop 1 done.
Loop 1 done.
Loop 1 done.
Loop 2 done.
Loop 1 done.
Loop 2 done.
Loop 2 done.
Loop 1 done.
Loop 2 done.
Loop 1 done.
Loop 2 done.
Loop 2 done.
Loop 2 done.
Loop 3 done.
Loop 2 done.
Loop 3 done.
Loop 3 done.
Loop 2 done.
Loop 3 done.
Loop 2 done.
Loop 3 done.
Loop 3 done.
Loop 3 done.
Loop 4 done.
Loop 3 done.
Loop 4 done.
Loop 4 done.
Loop 3 done.
Loop 4 done.
Loop 3 done.
Loop 4 done.
Loop 4 done.
Loop 4 done.
Loop 5 done.
Loop 4 done.
Loop 5 done.
Loop 5 done.
Loop 4 done.
Loop 5 done.
Loop 4 done.
Loop 5 done.
Loop 5 done.
Loop 5 done.
Loop 5 done.
Loop 5 done.
Loop 5 done.
2019-05-24 19:51:01 +0000
2019-05-24 19:51:15 +0000