在Swift(3.0)中提高高阶函数与循环的性能

时间:2016-11-24 14:34:46

标签: swift performance functional-programming

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.38s地图/滤波器,1.21s平面图。

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

在flatmap的情况下,它是先做地图,然后能够做一个更简单的过滤操作吗?

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

如果是这样,那么说编译器无法对此进行太多优化是否公平?

最后,还有更好的方法吗? HOF版本对我来说肯定更容易理解,但对于性能关键领域,它看起来像for循环是要走的路?

2 个答案:

答案 0 :(得分:3)

flatmap方法可能几乎等同于循环方法。从算法上讲,它是等价的。我想补充一点,即使是映射/过滤器方法,在这种情况下,应该“几乎”快,因为字符串操作占用了大量的运行时间。

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

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

答案 1 :(得分:0)

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

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