我有一个单元测试组,旨在证明并发执行多个重任务比串行执行更快。
现在...在所有人都因为上面的说法不总是正确而失去理智之前,因为多线程存在许多不确定性,请允许我解释一下。
从阅读苹果文档可以得知,当请求多个线程时,您无法保证获得多个线程。操作系统(iOS)会根据自己的判断分配线程。例如,如果设备只有一个核心,则会分配一个核心,并且由于并发操作的初始化代码需要一些额外的时间,而且不会提供性能提升,因此串行将稍微快一些。
然而,这种差异应该只是很小的。 但是,在我的 POC 设置中,差异非常大。 在我的 POC 中,并发需要比串行慢约三分之一的时间才能完成。
如果串行完成需要6秒,并发将需要9秒才能完成。
即使负载更重,这种趋势也会持续。 如果串行完成需要125秒,并发将需要215秒才能完成。 这种情况不仅发生一次,而且每次都如此。
我想知道在创建这个 POC 时是否犯了错误,如果是这样,我应该如何证明并发执行多个重任务确实比串行更快呢?
我的 swift 单元测试 POC:
func performHeavyTask(_ completion: (() -> Void)?) {
var counter = 0
while counter < 50000 {
print(counter)
counter = counter.advanced(by: 1)
}
completion?()
}
// MARK: - Serial
func testSerial () {
let start = DispatchTime.now()
let _ = DispatchQueue.global(qos: .userInitiated)
let mainDPG = DispatchGroup()
mainDPG.enter()
DispatchQueue.global(qos: .userInitiated).async {[weak self] in
guard let self = self else { return }
for _ in 0...10 {
self.performHeavyTask(nil)
}
mainDPG.leave()
}
mainDPG.wait()
let end = DispatchTime.now()
let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds // <<<<< Difference in nano seconds (UInt64)
print("NanoTime: \(nanoTime / 1_000_000_000)")
}
// MARK: - Concurrent
func testConcurrent() {
let start = DispatchTime.now()
let _ = DispatchQueue.global(qos: .userInitiated)
let mainDPG = DispatchGroup()
mainDPG.enter()
DispatchQueue.global(qos: .userInitiated).async {
let dispatchGroup = DispatchGroup()
let _ = DispatchQueue.global(qos: .userInitiated)
DispatchQueue.concurrentPerform(iterations: 10) { index in
dispatchGroup.enter()
self.performHeavyTask({
dispatchGroup.leave()
})
}
dispatchGroup.wait()
mainDPG.leave()
}
mainDPG.wait()
let end = DispatchTime.now()
let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds // <<<<< Difference in nano seconds (UInt64)
print("NanoTime: \(nanoTime / 1_000_000_000)")
}
细节:
操作系统:macOS High Sierra
型号名称:MacBook Pro
型号标识符:MacBookPro11,4
处理器名称:Intel Core i7
处理器速度:2.2 GHz
处理器数量:1
总核心数:4
两个测试都是在iPhone XS Max模拟器上完成的。两个测试都是在整个Mac重启后立即进行的(为了避免Mac忙于运行其他应用程序而不是运行此单元测试,从而使结果模糊)
此外,两个单元测试均包装在异步DispatcherWorkItem中,因为测试用例是为了防止主(UI)队列被阻塞,以防串行测试用例在这方面具有优势,因为它消耗主队列而不是像并发测试用例一样使用后台队列。
我还将接受一种可靠测试此功能的POC的答案。它不必始终显示并发比串行更快(请参阅上述解释)。但至少有时候是这样的。
testSerial
不是串行的,它是异步执行的 -DispatchQueue.global(qos: .userInitiated).async
sync
vs.async
testConcurrent
中做了一些非常奇怪的事情。print
函数移除并将数字增加到500000,再次测量,你会得到真正的差异:并发大约是串行的1/10。 - E.Coms