swift - NSOperationQueue 在计算任务上的性能比单线程差

标签 swift multithreading macos performance

我的第一个问题!

我正在对视频源进行 CPU 密集型图像处理,我想使用 OperationQueue。然而,结果绝对是可怕的。这是一个示例 - 假设我有一个 CPU 密集型操作:

var data = [Int].init(repeating: 0, count: 1_000_000)

func run() {
  let startTime = DispatchTime.now().uptimeNanoseconds
  for i in data.indices { data[i] = data[i] &+ 1 }
  NSLog("\(DispatchTime.now().uptimeNanoseconds - startTime)")
}

在我的笔记本电脑上执行大约需要 40 毫秒。我计时一百次:

(1...100).forEach { i in run(i) }

它们平均每个约 42 毫秒,总共约 4200 毫秒。我有 4 个物理内核,所以我尝试在 OperationQueue 上运行它:

var q = OperationQueue()
(1...100).forEach { i in
  q.addOperation {
    run(i)
  }
}
q.waitUntilAllOperationsAreFinished()

有趣的事情发生取决于 q.maxConcurrentOperationCount:

concurrency      single operation        total
     1                 45ms             4500ms
     2              100-250ms           8000ms
     3              100-300ms           7200ms
     4              250-450ms           9000ms
     5              250-650ms           9800ms
     6              600-800ms          11300ms

我使用 .background 的默认 QoS,可以看到线程优先级是默认值 (0.5)。查看 Instruments 的 CPU 利用率,我看到了很多浪费的周期(第一部分是在主线程上运行,第二部分是使用 OperationQueue 运行):

CPU Utilization

我用 C 编写了一个简单的线程队列,并在 Swift 中使用它,它随内核线性扩展,因此我的速度提高了 4 倍。但是我在 Swift 上做错了什么?

更新:我想我们已经得出结论,这是 DispatchQueue 中的一个合法错误。那么问题实际上是询问 DispatchQueue 代码中的问题的正确 channel 是什么?

最佳答案

您似乎测量了每次 run 执行的挂钟时间。这似乎不是正确的指标。并行化问题并不意味着每次运行都会执行得更快……它只是意味着您可以一次运行多次。

无论如何,让我验证你的结果。

您的函数 run 似乎只在某些时候采用参数。为了清楚起见,让我定义一个类似的函数:

func increment(_ offset : Int) {
  for i in data.indices { data[i] = data[i] &+ offset }
}

在我的测试机上,在 Release模式下,此代码每次输入需要 0.68 ns 或每次添加大约需要 2.3 个周期(在 3.4 GHz 时)。禁用边界检查会有所帮助(每个条目低至 0.5 ns)。

无论如何。那么接下来让我们按照您的建议将问题并行化:

var q = OperationQueue()
for i in 1...queues {
    q.addOperation {
      increment(i)
    }
}
q.waitUntilAllOperationsAreFinished()

这似乎不是特别安全,但速度快吗?

好吧,它更快了......我每次进入 0.3 ns。

源代码:https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/extra/swift/opqueue

关于swift - NSOperationQueue 在计算任务上的性能比单线程差,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41904500/

相关文章:

python - 在并发线程中使用类属性

macos - Grails 2.4.3 不适用于 mac os x

ios - WKWebView在催化剂上没有上下文菜单

c# - C# 中的密集文件 I/O 和数据处理

c - 获取线程状态,如 top

windows - 有没有一种简单的方法可以自动截取屏幕截图?

swift - Vapor 如何通过电子邮件查找用户

ios - Swift 3.0 向导航栏添加一个右键

ios - 用于渲染自定义 map (Spritekit 或原始 Metal )的有效 MacOS/iOS 框架?

ios - 如何从 GameScene 刷新 GameViewController?