【发布时间】:2019-07-05 05:10:33
【问题描述】:
我有一个单元测试设置来证明同时执行多个繁重的任务比串行更快。
现在...在大家对上述陈述并不总是正确的事实失去理智之前,让我解释一下。
我从阅读苹果文档中知道,您不能保证在请求多个线程时获得多个线程。操作系统(iOS)将分配线程,但它认为合适。例如,如果设备只有一个内核,它将分配一个内核,并且由于并发操作的初始化代码需要一些额外的时间,而串行会稍微快一些,而由于设备只有一个内核,因此不会提供性能改进。
但是:这种差异应该很小。但在我的 POC 设置中,差异是巨大的。在我的 POC 中,并发速度会慢大约 1/3。
如果串行在 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
处理器名称:英特尔酷睿 i7
处理器速度:2,2 GHz
处理器数量:1
核心总数:4
两项测试均在 iPhone XS Max 模拟器上完成。两个测试都是在整个 mac 重启后直接完成的(为了避免 mac 忙于运行这个单元测试以外的应用程序,模糊结果)
此外,两个单元测试都包装在异步 DispatcherWorkItem 中,因为测试用例用于不阻塞主(UI)队列,防止串行测试用例在该部分具有优势,因为它消耗主队列而不是与并发测试用例一样的后台队列。
我也将接受一个显示 POC 可靠地对此进行测试的答案。它不必一直显示并发比串行快(请阅读上面的解释以了解为什么不这样做)。但至少有一段时间
【问题讨论】:
-
对不起,您的测试没有任何意义:1.
testSerial不是串行的,它异步执行 -DispatchQueue.global(qos: .userInitiated).async2. 如果目标只是测试串行与. 并发,任务应该是相同的,唯一不同的是你如何将它添加到队列中 -sync与async3. 你在testConcurrent中做了一些非常奇怪的事情。 -
@mag_zbc 同样,两个单元测试都包装在异步 DispatcherWorkItem 中,因为测试用例用于不阻塞主(UI)队列,从而防止串行测试用例在该部分具有优势,因为它像并发测试用例那样使用主队列而不是后台队列。此外,并发的 2 个 DispatchGroups 是为了确保单元测试不会立即完成
-
在不竞争公共资源的情况下,并行任务比串行任务获得更高的性能。在您的 performHeavyTask() 函数中调用 print() 可能会导致这样的冲突,并且很可能是给您这些意外结果的原因。
-
@AlainT。有可能,没想到,什么是合适的 performHeavyTask 函数?
-
我同意原因是 print() 是这里真正的瓶颈,所有的速度都是由最慢的速度决定的。如果去掉
print,将数量增加到500000。再测一下,你就可以知道真正的区别:并发量大约是连续量的1/10。
标签: swift multithreading performance grand-central-dispatch