我有两个功能:
extension Array where Element: Hashable {
func uniqueOrderly() -> [Element] {
let startTime = CFAbsoluteTimeGetCurrent()
var set = Set<Element>()
var array = [Element]()
for element in self {
if set.contains(element) {
continue
}
set.insert(element)
array.append(element)
}
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time for uniqueOrderly: \(timeElapsed)")
return array
}
}
第二个:
public extension Sequence where Element: Equatable {
func unique() -> [Element] {
let startTime = CFAbsoluteTimeGetCurrent()
var unique: [Element] {
return reduce(into: []) {
unique, x in
if !unique.contains(x) {
unique.append(x)
}
}
}
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time for unique: \(timeElapsed)")
return unique
}
}
我正在对数组进行函数执行时间测量。
那是:
let arrayToFilter = [1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1,1,2,4,6,1,2,5,7,9,3,3,1]
实际的函数调用和结果按以下顺序排列:
arrayToFilter.unique() //Time for unique: 0.00012195110321044922
arrayToFilter.uniqueOrderly() Time for uniqueOrderly: 0.02329099178314209
但是当我更改函数顺序时,我的unique()
函数显示了可怕的时间测量结果。
arrayToFilter.uniqueOrderly() //Time for uniqueOrderly: 0.0013059377670288086
arrayToFilter.unique() //Time for unique: 8.940696716308594e-06
所以我的问题是为什么在不同的函数调用顺序下会出现这种情况?另外,当我在for循环中运行这些测试时,测量结果会大不相同。 (大约+-1秒)
所有测量者都在带有Release build设置(在模拟器上)的操场和真实的iOS应用程序中完成。
测试规范:
Xcode版本10.1
Swift 4.2
答案 0 :(得分:2)
您的unique()
实现在计算unique
之前先计算经过的时间。 unique
是一个计算得出的变量,在访问时会进行评估。
对于像这样的相当短的列表,您的uniqueOrderly()
实现仍然比unique()
慢,因为与几乎总是在前几个元素中命中的线性搜索相比,插入集合中的开销很大。 / p>
关于订购问题,几乎可以肯定这是测试方式的人工产物。微轮廓分析非常具有挑战性。我通过将每个文件放入自己的.swift
文件并以swift -O <file>
运行的方式进行了测试。但是,如果我按照自己的方式将它们放到同一个文件中,并修复unique()
以实际计时其活动,那么在以swift -O <file>
运行时,时间是相当一致的:
Time for uniqueOrderly: 1.800060272216797e-05
[1, 2, 4, 6, 5, 7, 9, 3]
Time for unique: 2.0265579223632812e-06
[1, 2, 4, 6, 5, 7, 9, 3]
Time for unique: 2.002716064453125e-05
[1, 2, 4, 6, 5, 7, 9, 3]
Time for uniqueOrderly: 2.9802322387695312e-06
[1, 2, 4, 6, 5, 7, 9, 3]
经过更多测试之后,我怀疑这些差异部分是内存分配(内存是从操作系统中按块获取的,因此第一个分配器要比第二个分配器付出更大的代价)和L1缓存。如果对两个不同的数组进行排序(指向内存缓存),几乎所有差异都会消失。但是,由于该算法有很多重复项,因此它对数组的组成也非常敏感。
无论如何,这种微观优化是没有意义的。您将整日追逐鬼魂。很难在微小的数据块上对微小的代码段进行性能测试,并以适用于实际使用的方式对其进行优化。至少,您需要更多,更大的数组,并需要对许多不同种类的发行版进行测试(很多重复项或很少重复项)。您必须在Playgrounds之外进行测试,并且必须使用优化器。