数组上的 Swift 减少比不安全的减少慢几个数量级

Lou*_*Lac 7 optimization reduce swift

在我的程序中,我严重依赖以下我认为非常常见的模式。有一个保存数据的结构(可以说Double):

struct Container {
  var data: Double
}
Run Code Online (Sandbox Code Playgroud)

接下来,创建该结构的数组,可能很大:

let containers = (0...<10_000_000).map { _ in 
  Container(data: .random(in: -10...10))
}
Run Code Online (Sandbox Code Playgroud)

然后,对数组应用 (map)reduce 操作以提取值,例如:

let sum = containers.reduce(0.0) { $0 + $1.data }
Run Code Online (Sandbox Code Playgroud)

我最初认为这个特定的操作会很快,因为该操作足够简单,编译器可以优化边界检查和其他慢速操作:没有奇怪的索引计算,闭包中没有捕获,一切都是不可变的,等等......

但是,我将性能与这种不安全的 reduce 数组实现进行了比较:

extension Array {
  func unsafeReduce<Result>(
    _ initialResult: Result,
    _ nextPartialResult: (Result, Element) throws -> Result
  ) rethrows -> Result {
    try self.withUnsafeBufferPointer { buffer -> Result in
      try buffer.reduce(initialResult, nextPartialResult)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

我用完全优化编译了两个程序,-O -unchecked我还使用了 Xcode 分析器来对程序进行计时,看来不安全的减少比安全的减少快 16 倍: 200 毫秒与 4 秒。

我的问题是:

  • 首先,我的不安全实现是否正确?使用这个版本有什么潜力?
  • 放缓的原因是什么?我尝试使用 Compiler Explorer 和 Xcode profiler 的调用树查看汇编代码,但无法弄清楚确切原因是什么。有人可以逐步解释该程序到底是做什么的吗?

我在 2015 年年中的 MacBook Air 上使用 Xcode 11.5 和 Swift 5。


编辑

我将代码移植到 Python,并且没有使用 Numpy 进行任何计算,它仍然比 Swiftreduce操作快 2 倍。不过,我没有检查内存消耗。