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循环是要走的路?
答案 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)