【问题标题】:Improving performance of higher order functions vs loops in Swift (3.0)提高 Swift (3.0) 中高阶函数与循环的性能
【发布时间】:2016-11-24 14:34:45
【问题描述】:
import XCTest

class testTests: XCTestCase {
    static func makearray() -> [Int] {
        var array: [Int] = []
        for x in 0..<1000000 {
           array.append(x)
        }
        return array
    }

    let array = testTests.makearray()

    func testPerformanceExample() {
        self.measure {
            var result: [String] = []
            for x in self.array {
            let tmp = "string\(x)"
            if tmp.hasSuffix("1234") {
                result.append(tmp)
            }
        }
        print(result.count)
    }
}

func testPerformanceExample2() {
    self.measure {
        let result = self.array.map { "string\($0)" }
                               .filter { $0.hasSuffix("1234") }
        print(result.count)
    }
}

func testPerformanceExample3() {
    self.measure {
        let result = self.array.flatMap { int -> String? in
            let tmp = "string\(int)"
            return tmp.hasSuffix("1234") ? tmp : nil
        }
        print(result.count)
    }
}
}

在这段代码中,我试图了解高阶函数在处理大型数组方面的表现。

3 次测试产生相同的结果,循环时间约为 0.75 秒,地图/过滤器时间为 1.38 秒,平面地图时间为 1.21 秒。

假设 HOF 或多或少是包装循环的函数,这在 map/filter 的情况下是有意义的,它循环遍历 map 的第一个数组,然后循环遍历其结果以进行过滤。

在flatmap的情况下,是不是先做map,再做更简单的filter操作?

我对幕后发生的事情的理解(大致)正确吗?

如果是这样,是否可以说编译器无法对此进行太多优化?

最后,有没有更好的方法来做到这一点? HOF 版本对我来说肯定更容易理解,但对于性能关键领域,看起来 for-loops 是要走的路?

【问题讨论】:

  • 您是否真的遇到过这很重要的情况,或者这只是理论上的情况?
  • 此外,如果性能真的很关键,那么您肯定会直接写入内存。所以我很难相信这绝不是浪费时间。
  • 你真的告诉编译器进行优化吗?如果没有,这一切都毫无意义,不是吗?
  • 你可以在github.com/apple/swift/blob/master/stdlib/public/core/…找到flatMap的源代码,它是单通道的,不是map+filter。 – 如果 map 明显慢于等效的显式循环(在优化代码中),那么您可能需要提交错误报告。
  • 抱歉,马特,但我不确定你在说什么?我确实有需要处理大量数据的情况。

标签: swift performance functional-programming


【解决方案1】:

平面图方法可能几乎等同于循环方法。在算法上,它是等价的。我要补充一点,在这种情况下,即使是 map/filter 方法,也应该“几乎”与字符串操作所占用的大部分运行时间一样快。

为了获得良好的性能,人们希望避免使用临时字符串。我们可以达到预期的效果如下...

func fastloopflatmap (_ test_array: [Int]) -> [String] {
   var result: [String] = []
    for x in array {
      if x % 10000 == 1234 {
          result.append("string\(x)")
      }
    }
    return result;
}

这是我的时间安排:

loop        : 632 ns/element
filter/map  : 650 ns/element
flatmap     : 632 ns/element
fast loop   : 1.2 ns/element

因此,如您所见,慢速函数的大部分 (99%) 运行时间是由于对临时字符串的操作。

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

【讨论】:

    【解决方案2】:

    从技术上讲,如果编译器替换为并行使用多个内核的实现,这些 HOF 可能具有更好的 Big O 性能。但是,Swift 目前不这样做。更进一步的是使用粒度控制来适当地使用迭代或并行实现,基于权衡并行化的开销成本与输入大小:

    https://en.wikipedia.org/wiki/Granularity_(parallel_computing)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-08-25
      • 2014-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-29
      • 2018-11-08
      • 1970-01-01
      相关资源
      最近更新 更多