- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我有一个单元测试设置来证明同时执行多个繁重的任务比串行执行更快。
现在...在座的每个人都因为上面的说法并不总是正确的事实而失去理智,因为多线程具有许多不确定性,让我解释一下。
我从阅读 apple 文档中了解到,您不能保证在请求时获得多个线程。操作系统 (iOS) 将按其认为合适的方式分配线程。例如,如果设备只有一个内核,它将分配一个内核,并且由于并发操作的初始化代码需要一些额外的时间,因此串行会稍微快一些,同时由于设备只有一个内核而没有提供性能改进。
但是:这种差异应该很小。但在我的 POC 设置中,差异很大。在我的 POC 中,并发速度慢了大约 1/3 的时间。
如果串行在 6 秒 内完成,并发将在 9 秒 内完成。
即使负载更重,这种趋势也会继续。如果串行在 125 秒 内完成,并发将在 215 秒 内竞争。这也不仅会发生一次,而且每次都会发生。
我想知道我是否在创建这个 POC 时犯了错误,如果是,我应该如何证明并发执行多个繁重的任务确实比串行执行更快?
我在快速单元测试中的 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 可靠地对此进行测试的答案。它不必一直显示并发比串行快(阅读上面关于为什么不这样做的解释)。但至少有一段时间
最佳答案
有两个问题:
我会避免做 print
在循环内。这是同步的,您可能会在并发实现中遇到更大的性能下降。这不是故事的全部,但这并没有帮助。
即使删除了 print
从循环内部来看,计数器的 50,000 个增量根本不足以看到 concurrentPerform
的好处。 .作为Improving on Loop Code说:
... And although this [
concurrentPerform
] can be a good way to improve performance in loop-based code, you must still use this technique discerningly. Although dispatch queues have very low overhead, there are still costs to scheduling each loop iteration on a thread. Therefore, you should make sure your loop code does enough work to warrant the costs. Exactly how much work you need to do is something you have to measure using the performance tools.
在调试构建中,我需要将迭代次数增加到接近 5,000,000 的值才能克服此开销。在发布版本中,即使这样还不够。旋转循环和增加计数器的速度太快,无法对并发行为进行有意义的分析。
因此,在我下面的示例中,我将这个旋转循环替换为计算量更大的计算(使用历史悠久但效率不高的算法计算 π)。
顺便说一句:
与其自己衡量性能,不如在 XCTestCase
内执行此操作单元测试,可以用 measure
基准性能。这会多次重复基准测试、捕获耗时、计算结果的平均值等。只需确保编辑您的方案,以便测试操作使用优化的“发布”构建而不是“调试”构建。
如果您打算使用调度组让调用线程等待它完成,那么将其调度到全局队列是没有意义的。
您不需要使用调度组来等待 concurrentPerform
完成,要么。它同步运行。
作为 concurrentPerform
documentation说:
The dispatch queue executes the submitted block the specified number of times and waits for all iterations to complete before returning.
这不是很重要,但值得注意的是你的 for _ in 0...10 { ... }
正在进行 11 次迭代,而不是 10 次。您显然打算使用 ..<
.
因此,这里有一个示例,将其放入单元测试中,但用计算量更大的东西代替“繁重”的计算:
class MyAppTests: XCTestCase {
// calculate pi using Gregory-Leibniz series
func calculatePi(iterations: Int) -> Double {
var result = 0.0
var sign = 1.0
for i in 0 ..< iterations {
result += sign / Double(i * 2 + 1)
sign *= -1
}
return result * 4
}
func performHeavyTask(iteration: Int) {
let pi = calculatePi(iterations: 100_000_000)
print(iteration, .pi - pi)
}
func testSerial() {
measure {
for i in 0..<10 {
self.performHeavyTask(iteration: i)
}
}
}
func testConcurrent() {
measure {
DispatchQueue.concurrentPerform(iterations: 10) { i in
self.performHeavyTask(iteration: i)
}
}
}
}
在我配备 2.9 GHz Intel Core i9 的 MacBook Pro 2018 上,发布版本的并发测试平均耗时 0.247 秒,而串行测试耗时大约是其四倍,即 1.030 秒。
关于swift - 调度队列 : why does serial complete faster than concurrent?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54635103/
我有一个带有一些功能的perl对象。每个功能从主程序中调用一次。我想并行运行某些功能以节省时间。由于某些功能取决于先前功能的结果,因此我无法将它们全部一起运行。 我想到了这样的事情: 对于每个函数,保
首先,我的代码在这里: import schedule # see https://github.com/dbader/schedule import crawler def job(): p
从 11 月 1 日开始,我必须使用quartz调度程序每4个月安排一次任务。我使用 cronExpression 来实现同样的目的。但 cronExpression 每年都会重置。所以我的任务将在
我有以下代码块,它调用两个请求,但略有延迟。 final ActorRef actor1 = getContext().actorOf( ActorClass.prop
考虑到 Linux 的情况,我们为每个用户堆栈都有一个内核堆栈,据我所知,每当发生上下文切换时,我们都会切换到当前进程的内核模式。 这里我们保存当前进程的当前状态,寄存器,程序数据等,然后调度器(不确
我有将东西移植到 OpenBSD 的奇怪爱好。我知道它有 pthreads 问题,但在 2013 年 5 月发布版本之前我不会升级。我使用的是 5.0,我对 pthreads 还很陌生。我已经学习了
给定一组任务: T1(20,100) T2(30,250) T3(100,400) (execution time, deadline=peroid) 现在我想将截止日期限制为 Di = f * Pi
使用 Django 开发一个小型日程安排 Web 应用程序,在该应用程序中,人们被分配特定的时间与他们的上级会面。员工存储为模型,与表示时间范围和他们有空的星期几的模型具有 OneToMany 关系。
我想了解贪婪算法调度问题的工作原理。 所以我一直在阅读和谷歌搜索一段时间,因为我无法理解贪心算法调度问题。 我们有 n 个作业要安排在单个资源上。作业 (i) 有一个请求的开始时间 s(i) 和结束时
这是流行的 El Goog 问题的变体。 考虑以下调度问题:有 n 个作业,i = 1..n。有 1 台 super 计算机和无限的 PC。每个作业都需要先经过 super 计算机的预处理,然后再在P
假设我有一个需要运行多次的蜘蛛 class My_spider(Scrapy.spider): #spider def 我想做这样的事 while True: runner = Cra
我已将 podAntiAffinity 添加到我的 DeploymentConfig 模板中。 但是,pod 被安排在我预计会被规则排除的节点上。 我如何查看 kubernetes 调度程序的日志以了
我已经使用 React - Redux - Typescript 堆栈有一段时间了,到目前为止我很喜欢它。但是,由于我对 Redux 很陌生,所以我一直在想这个特定的话题。 调度 Redux 操作(和
我想按照预定的计划(例如,周一至周五,美国东部时间晚上 9 点至 5 点)运行单个 Azure 实例以减少账单,并且想知道最好的方法是什么。 问题的两个部分: 能否使用服务管理 API [1] 按预定
假设最小模块安装(为了简单起见),Drupal 的 index.php 中两个顶级功能的核心“职责”是什么? ? drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); me
我正在尝试使用 Racket(以前称为 PLT Scheme)连接 URL 调度。我查看了教程和服务器文档。我不知道如何将请求路由到相同的 servlet。 具体例子: #lang 方案 (需要网络服
我想在 Airflow (v1.9.0) 上运行计划。 我的DAG需要在每个月底运行,但我不知道如何编写设置。 my_dag = DAG(dag_id=DAG_ID, cat
我正在尝试在“httpTrigger”类型函数的 function.json 中设置计划字段,但计时器功能似乎未运行。我的目标是拥有一个甚至可以在需要时进行调度和手动启动的功能,而不必仅为了调度而添加
我正在尝试制定每周、每月的 Airflow 计划,但不起作用。有人可以报告可能发生的情况吗?如果我每周、每月进行安排,它就会保持静止,就好像它被关闭一样。没有错误信息,只是不执行。我发送了一个代码示例
我希望每两周自动更新一次我的表格。我希望我的函数能够被 firebase 调用。 这可能吗? 我正在使用 Angular 2 Typescript 和 Firebase。 最佳答案 仅通过fireba
我是一名优秀的程序员,十分优秀!